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 * pm This driver now only handles the ioctl interface. The scanning 31 * and policy stuff now lives in common/os/sunpm.c. 32 * Not DDI compliant 33 */ 34 35 #include <sys/types.h> 36 #include <sys/errno.h> 37 #include <sys/modctl.h> 38 #include <sys/conf.h> /* driver flags and functions */ 39 #include <sys/open.h> /* OTYP_CHR definition */ 40 #include <sys/stat.h> /* S_IFCHR definition */ 41 #include <sys/pathname.h> /* name -> dev_info xlation */ 42 #include <sys/kmem.h> /* memory alloc stuff */ 43 #include <sys/debug.h> 44 #include <sys/pm.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/epm.h> 48 #include <sys/vfs.h> 49 #include <sys/mode.h> 50 #include <sys/mkdev.h> 51 #include <sys/promif.h> 52 #include <sys/consdev.h> 53 #include <sys/ddi_impldefs.h> 54 #include <sys/poll.h> 55 #include <sys/note.h> 56 #include <sys/taskq.h> 57 #include <sys/policy.h> 58 59 /* 60 * Minor number is instance<<8 + clone minor from range 1-255; (0 reserved 61 * for "original" 62 */ 63 #define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE - 1)) 64 65 #define PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components) 66 #define PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB) 67 #define PM_MAJOR(dip) ddi_driver_major(dip) 68 #define PM_RELE(dip) ddi_release_devi(dip) 69 70 #define PM_IDLEDOWN_TIME 10 71 72 extern kmutex_t pm_scan_lock; /* protects autopm_enable, pm_scans_disabled */ 73 extern kmutex_t pm_clone_lock; /* protects pm_clones array */ 74 extern int autopm_enabled; 75 extern kcondvar_t pm_clones_cv[PM_MAX_CLONE]; 76 extern uint_t pm_poll_cnt[PM_MAX_CLONE]; 77 78 /* 79 * The soft state of the power manager. Since there will only 80 * one of these, just reference it through a static pointer. 81 */ 82 static struct pmstate { 83 dev_info_t *pm_dip; /* ptr to our dev_info node */ 84 int pm_instance; /* for ddi_get_instance() */ 85 timeout_id_t pm_idledown_id; /* pm idledown timeout id */ 86 uchar_t pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */ 87 struct cred *pm_cred[PM_MAX_CLONE]; /* cred for each unique open */ 88 } pm_state = { NULL, -1, (timeout_id_t)0 }; 89 typedef struct pmstate *pm_state_t; 90 static pm_state_t pmstp = &pm_state; 91 92 static int pm_open(dev_t *, int, int, cred_t *); 93 static int pm_close(dev_t, int, int, cred_t *); 94 static int pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 95 static int pm_chpoll(dev_t, short, int, short *, struct pollhead **); 96 97 static struct cb_ops pm_cb_ops = { 98 pm_open, /* open */ 99 pm_close, /* close */ 100 nodev, /* strategy */ 101 nodev, /* print */ 102 nodev, /* dump */ 103 nodev, /* read */ 104 nodev, /* write */ 105 pm_ioctl, /* ioctl */ 106 nodev, /* devmap */ 107 nodev, /* mmap */ 108 nodev, /* segmap */ 109 pm_chpoll, /* poll */ 110 ddi_prop_op, /* prop_op */ 111 NULL, /* streamtab */ 112 D_NEW | D_MP /* driver compatibility flag */ 113 }; 114 115 static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 116 void **result); 117 static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 118 static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 119 120 static struct dev_ops pm_ops = { 121 DEVO_REV, /* devo_rev */ 122 0, /* refcnt */ 123 pm_getinfo, /* info */ 124 nulldev, /* identify */ 125 nulldev, /* probe */ 126 pm_attach, /* attach */ 127 pm_detach, /* detach */ 128 nodev, /* reset */ 129 &pm_cb_ops, /* driver operations */ 130 NULL, /* bus operations */ 131 NULL /* power */ 132 }; 133 134 static struct modldrv modldrv = { 135 &mod_driverops, 136 "power management driver v%I%", 137 &pm_ops 138 }; 139 140 static struct modlinkage modlinkage = { 141 MODREV_1, &modldrv, 0 142 }; 143 144 /* Local functions */ 145 #ifdef DEBUG 146 static int print_info(dev_info_t *, void *); 147 148 #endif 149 150 int 151 _init(void) 152 { 153 return (mod_install(&modlinkage)); 154 } 155 156 int 157 _fini(void) 158 { 159 return (mod_remove(&modlinkage)); 160 } 161 162 int 163 _info(struct modinfo *modinfop) 164 { 165 return (mod_info(&modlinkage, modinfop)); 166 } 167 168 static int 169 pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 170 { 171 int i; 172 173 switch (cmd) { 174 175 case DDI_ATTACH: 176 if (pmstp->pm_instance != -1) /* Only allow one instance */ 177 return (DDI_FAILURE); 178 pmstp->pm_instance = ddi_get_instance(dip); 179 if (ddi_create_minor_node(dip, "pm", S_IFCHR, 180 (pmstp->pm_instance << 8) + 0, 181 DDI_PSEUDO, 0) != DDI_SUCCESS) { 182 return (DDI_FAILURE); 183 } 184 pmstp->pm_dip = dip; /* pm_init and getinfo depend on it */ 185 186 for (i = 0; i < PM_MAX_CLONE; i++) 187 cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL); 188 189 ddi_report_dev(dip); 190 return (DDI_SUCCESS); 191 192 default: 193 return (DDI_FAILURE); 194 } 195 } 196 197 /* ARGSUSED */ 198 static int 199 pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 200 { 201 int i; 202 203 switch (cmd) { 204 case DDI_DETACH: 205 /* 206 * Don't detach while idledown timeout is pending. Note that 207 * we already know we're not in pm_ioctl() due to framework 208 * synchronization, so this is a sufficient test 209 */ 210 if (pmstp->pm_idledown_id) 211 return (DDI_FAILURE); 212 213 for (i = 0; i < PM_MAX_CLONE; i++) 214 cv_destroy(&pm_clones_cv[i]); 215 216 ddi_remove_minor_node(dip, NULL); 217 pmstp->pm_instance = -1; 218 return (DDI_SUCCESS); 219 220 default: 221 return (DDI_FAILURE); 222 } 223 } 224 225 static int 226 pm_close_direct_pm_device(dev_info_t *dip, void *arg) 227 { 228 int clone; 229 char *pathbuf; 230 pm_info_t *info = PM_GET_PM_INFO(dip); 231 232 clone = *((int *)arg); 233 234 if (!info) 235 return (DDI_WALK_CONTINUE); 236 237 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 238 PM_LOCK_DIP(dip); 239 if (clone == info->pmi_clone) { 240 PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n", 241 PM_DEVICE(dip))) 242 ASSERT(PM_ISDIRECT(dip)); 243 info->pmi_dev_pm_state &= ~PM_DIRECT; 244 PM_UNLOCK_DIP(dip); 245 pm_proceed(dip, PMP_RELEASE, -1, -1); 246 /* Bring ourselves up if there is a keeper that is up */ 247 (void) ddi_pathname(dip, pathbuf); 248 pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL, 249 pathbuf, PM_DEP_NOWAIT, NULL, 0); 250 PM_LOCK_DIP(dip); 251 info->pmi_clone = 0; 252 PM_UNLOCK_DIP(dip); 253 } else { 254 PM_UNLOCK_DIP(dip); 255 } 256 kmem_free(pathbuf, MAXPATHLEN); 257 258 /* restart autopm on device released from direct pm */ 259 pm_rescan(dip); 260 261 return (DDI_WALK_CONTINUE); 262 } 263 264 #define PM_REQ 1 265 #define NOSTRUCT 2 266 #define DIP 3 267 #define NODIP 4 268 #define NODEP 5 269 #define DEP 6 270 #define PM_PSC 7 271 272 #define CHECKPERMS 0x001 273 #define SU 0x002 274 #define SG 0x004 275 #define OWNER 0x008 276 277 #define INWHO 0x001 278 #define INDATAINT 0x002 279 #define INDATASTRING 0x004 280 #define INDEP 0x008 281 #define INDATAOUT 0x010 282 #define INDATA (INDATAOUT | INDATAINT | INDATASTRING | INDEP) 283 284 struct pm_cmd_info { 285 int cmd; /* command code */ 286 char *name; /* printable string */ 287 int supported; /* true if still supported */ 288 int str_type; /* PM_REQ or NOSTRUCT */ 289 int inargs; /* INWHO, INDATAINT, INDATASTRING, INDEP, */ 290 /* INDATAOUT */ 291 int diptype; /* DIP or NODIP */ 292 int deptype; /* DEP or NODEP */ 293 int permission; /* SU, GU, or CHECKPERMS */ 294 }; 295 296 #ifdef DEBUG 297 char *pm_cmd_string; 298 int pm_cmd; 299 #endif 300 301 /* 302 * Returns true if permission granted by credentials 303 */ 304 static int 305 pm_perms(int perm, cred_t *cr) 306 { 307 if (perm == 0) /* no restrictions */ 308 return (1); 309 if (perm == CHECKPERMS) /* ok for now (is checked later) */ 310 return (1); 311 if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */ 312 return (1); 313 if ((perm & SG) && (crgetgid(cr) == 0)) /* group 0 is ok */ 314 return (1); 315 return (0); 316 } 317 318 #ifdef DEBUG 319 static int 320 print_info(dev_info_t *dip, void *arg) 321 { 322 _NOTE(ARGUNUSED(arg)) 323 pm_info_t *info; 324 int i, j; 325 struct pm_component *cp; 326 extern int pm_cur_power(pm_component_t *cp); 327 328 info = PM_GET_PM_INFO(dip); 329 if (!info) 330 return (DDI_WALK_CONTINUE); 331 cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip)); 332 for (i = 0; i < PM_NUMCMPTS(dip); i++) { 333 cp = PM_CP(dip, i); 334 cmn_err(CE_CONT, "\tThresholds[%d] =", i); 335 for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++) 336 cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]); 337 cmn_err(CE_CONT, "\n"); 338 cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i, 339 pm_cur_power(cp)); 340 } 341 if (PM_ISDIRECT(dip)) 342 cmn_err(CE_CONT, "\tDirect power management\n"); 343 return (DDI_WALK_CONTINUE); 344 } 345 #endif 346 347 /* 348 * command, name, supported, str_type, inargs, diptype, deptype, permission 349 */ 350 static struct pm_cmd_info pmci[] = { 351 {PM_SCHEDULE, "PM_SCHEDULE", 0}, 352 {PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0}, 353 {PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0}, 354 {PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0}, 355 {PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0}, 356 {PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0}, 357 {PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0}, 358 {PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0}, 359 {PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0}, 360 {PM_GET_DEP, "PM_GET_DEP", 0}, 361 {PM_ADD_DEP, "PM_ADD_DEP", 0}, 362 {PM_REM_DEP, "PM_REM_DEP", 0}, 363 {PM_REM_DEVICE, "PM_REM_DEVICE", 0}, 364 {PM_REM_DEVICES, "PM_REM_DEVICES", 0}, 365 {PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP, 366 NODEP}, 367 {PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0}, 368 {PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0}, 369 {PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 }, 370 {PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ, 371 INWHO, NODIP, NODEP, SU}, 372 {PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT}, 373 {PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD", 374 1, NOSTRUCT}, 375 {PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT, 376 0, 0, 0, SU}, 377 {PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU}, 378 {PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU}, 379 {PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU}, 380 {PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT, 381 DIP, NODEP}, 382 {PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO, 383 DIP, NODEP}, 384 {PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT, 385 DIP, NODEP}, 386 {PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ, 387 INWHO | INDATAOUT, DIP, NODEP}, 388 {PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO, 389 DIP, NODEP}, 390 {PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ, 391 INWHO | INDATAOUT, DIP, NODEP}, 392 {PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO, 393 DIP, NODEP}, 394 {PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC}, 395 {PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC}, 396 {PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP, 397 (SU | SG)}, 398 {PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO, 399 DIP, NODEP}, 400 {PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC}, 401 {PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC}, 402 {PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ, 403 INWHO, DIP, NODEP, SU}, 404 {PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT}, 405 {PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO, 406 DIP, NODEP}, 407 {PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ, 408 INWHO | INDATAINT, NODIP, NODEP, SU}, 409 {PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ, 410 INWHO | INDATAOUT, DIP, NODEP}, 411 {PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU}, 412 {PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1, 413 PM_REQ, INWHO, DIP, NODEP}, 414 {PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP, 415 NODEP}, 416 {PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP, 417 NODEP}, 418 {PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP, 419 NODEP}, 420 {PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING, 421 DIP, DEP, SU}, 422 {PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP}, 423 {PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ, 424 INWHO | INDATASTRING, NODIP, DEP, SU}, 425 {0, NULL} 426 }; 427 428 struct pm_cmd_info * 429 pc_info(int cmd) 430 { 431 struct pm_cmd_info *pcip; 432 433 for (pcip = pmci; pcip->name; pcip++) { 434 if (cmd == pcip->cmd) 435 return (pcip); 436 } 437 return (NULL); 438 } 439 440 static char * 441 pm_decode_cmd(int cmd) 442 { 443 static char invbuf[64]; 444 struct pm_cmd_info *pcip = pc_info(cmd); 445 if (pcip != NULL) 446 return (pcip->name); 447 (void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd); 448 return (invbuf); 449 } 450 451 /* 452 * Allocate scan resource, create taskq, then dispatch scan, 453 * called only if autopm is enabled. 454 */ 455 int 456 pm_start_pm_walk(dev_info_t *dip, void *arg) 457 { 458 char *cmdstr = pm_decode_cmd(*((int *)arg)); 459 460 if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) 461 return (DDI_WALK_CONTINUE); 462 463 /* 464 * Construct per dip scan taskq 465 */ 466 mutex_enter(&pm_scan_lock); 467 if (autopm_enabled) 468 pm_scan_init(dip); 469 mutex_exit(&pm_scan_lock); 470 471 /* 472 * Start doing pm on device: ensure pm_scan data structure initiated, 473 * no need to gurantee a successful scan run. 474 */ 475 PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr, 476 PM_DEVICE(dip))) 477 pm_rescan(dip); 478 479 return (DDI_WALK_CONTINUE); 480 } 481 482 /* 483 * Bring devices to full power level, then stop scan 484 */ 485 int 486 pm_stop_pm_walk(dev_info_t *dip, void *arg) 487 { 488 pm_info_t *info = PM_GET_PM_INFO(dip); 489 char *cmdstr = pm_decode_cmd(*((int *)arg)); 490 491 if (!info) 492 return (DDI_WALK_CONTINUE); 493 /* 494 * Stop the current scan, and then bring it back to normal power. 495 */ 496 if (!PM_ISBC(dip)) { 497 PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for " 498 "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip))) 499 pm_scan_stop(dip); 500 } 501 502 if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) && 503 !pm_all_at_normal(dip)) { 504 PM_LOCK_DIP(dip); 505 if (info->pmi_dev_pm_state & PM_DETACHING) { 506 PMD(PMD_ALLNORM, ("ioctl: %s: deferring " 507 "all_to_normal because %s@%s(%s#%d) is detaching\n", 508 cmdstr, PM_DEVICE(dip))) 509 info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED; 510 PM_UNLOCK_DIP(dip); 511 return (DDI_WALK_CONTINUE); 512 } 513 PM_UNLOCK_DIP(dip); 514 if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) { 515 PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s" 516 "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip))) 517 } 518 } 519 520 return (DDI_WALK_CONTINUE); 521 } 522 523 static int 524 pm_start_idledown(dev_info_t *dip, void *arg) 525 { 526 int flag = (int)(intptr_t)arg; 527 pm_scan_t *scanp = PM_GET_PM_SCAN(dip); 528 529 if (!scanp) 530 return (DDI_WALK_CONTINUE); 531 532 PM_LOCK_DIP(dip); 533 scanp->ps_idle_down |= flag; 534 PM_UNLOCK_DIP(dip); 535 pm_rescan(dip); 536 537 return (DDI_WALK_CONTINUE); 538 } 539 540 /*ARGSUSED*/ 541 static int 542 pm_end_idledown(dev_info_t *dip, void *ignore) 543 { 544 pm_scan_t *scanp = PM_GET_PM_SCAN(dip); 545 546 if (!scanp) 547 return (DDI_WALK_CONTINUE); 548 549 PM_LOCK_DIP(dip); 550 /* 551 * The PMID_TIMERS bits are place holder till idledown expires. 552 * The bits are also the base for regenerating PMID_SCANS bits. 553 * While it's up to scan thread to clear up the PMID_SCANS bits 554 * after each scan run, PMID_TIMERS ensure aggressive scan down 555 * performance throughout the idledown period. 556 */ 557 scanp->ps_idle_down &= ~PMID_TIMERS; 558 PM_UNLOCK_DIP(dip); 559 560 return (DDI_WALK_CONTINUE); 561 } 562 563 /*ARGSUSED*/ 564 static void 565 pm_end_idledown_walk(void *ignore) 566 { 567 PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is " 568 "off\n", (ulong_t)pmstp->pm_idledown_id)); 569 570 mutex_enter(&pm_scan_lock); 571 pmstp->pm_idledown_id = 0; 572 mutex_exit(&pm_scan_lock); 573 574 ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL); 575 } 576 577 /* 578 * pm_timeout_idledown - keep idledown effect for 10 seconds. 579 * 580 * Return 0 if another competing caller scheduled idledown timeout, 581 * otherwise, return idledown timeout_id. 582 */ 583 static timeout_id_t 584 pm_timeout_idledown(void) 585 { 586 timeout_id_t to_id; 587 588 /* 589 * Keep idle-down in effect for either 10 seconds 590 * or length of a scan interval, which ever is greater. 591 */ 592 mutex_enter(&pm_scan_lock); 593 if (pmstp->pm_idledown_id != 0) { 594 to_id = pmstp->pm_idledown_id; 595 pmstp->pm_idledown_id = 0; 596 mutex_exit(&pm_scan_lock); 597 (void) untimeout(to_id); 598 mutex_enter(&pm_scan_lock); 599 if (pmstp->pm_idledown_id != 0) { 600 PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: " 601 "another caller got it, idledown_id(%lx)!\n", 602 (ulong_t)pmstp->pm_idledown_id)) 603 mutex_exit(&pm_scan_lock); 604 return (0); 605 } 606 } 607 pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL, 608 PM_IDLEDOWN_TIME * hz); 609 PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n", 610 (ulong_t)pmstp->pm_idledown_id)) 611 mutex_exit(&pm_scan_lock); 612 613 return (pmstp->pm_idledown_id); 614 } 615 616 static int 617 pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp, 618 struct pollhead **phpp) 619 { 620 extern struct pollhead pm_pollhead; /* common/os/sunpm.c */ 621 int clone; 622 623 clone = PM_MINOR_TO_CLONE(getminor(dev)); 624 PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone)) 625 if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) { 626 *reventsp |= (POLLIN | POLLRDNORM); 627 PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n")) 628 } else { 629 *reventsp = 0; 630 if (!anyyet) { 631 PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n")) 632 *phpp = &pm_pollhead; 633 } 634 #ifdef DEBUG 635 else { 636 PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n")) 637 } 638 #endif 639 } 640 return (0); 641 } 642 643 /* 644 * called by pm_dicard_entries to free up the memory. It also decrements 645 * pm_poll_cnt, if direct is non zero. 646 */ 647 static void 648 pm_free_entries(psce_t *pscep, int clone, int direct) 649 { 650 pm_state_change_t *p; 651 652 if (pscep) { 653 p = pscep->psce_out; 654 while (p->size) { 655 if (direct) { 656 PMD(PMD_IOCTL, ("ioctl: discard: " 657 "pm_poll_cnt[%d] is %d before " 658 "ASSERT\n", clone, 659 pm_poll_cnt[clone])) 660 ASSERT(pm_poll_cnt[clone]); 661 pm_poll_cnt[clone]--; 662 } 663 kmem_free(p->physpath, p->size); 664 p->size = 0; 665 if (p == pscep->psce_last) 666 p = pscep->psce_first; 667 else 668 p++; 669 } 670 pscep->psce_out = pscep->psce_first; 671 pscep->psce_in = pscep->psce_first; 672 mutex_exit(&pscep->psce_lock); 673 } 674 } 675 676 /* 677 * Discard entries for this clone. Calls pm_free_entries to free up memory. 678 */ 679 static void 680 pm_discard_entries(int clone) 681 { 682 psce_t *pscep; 683 psce_t *pm_psc_clone_to_direct(int); 684 psce_t *pm_psc_clone_to_interest(int); 685 int direct = 0; 686 687 mutex_enter(&pm_clone_lock); 688 if ((pscep = pm_psc_clone_to_direct(clone)) != NULL) 689 direct = 1; 690 pm_free_entries(pscep, clone, direct); 691 pscep = pm_psc_clone_to_interest(clone); 692 pm_free_entries(pscep, clone, 0); 693 mutex_exit(&pm_clone_lock); 694 } 695 696 int 697 pm_set_sys_threshold(dev_info_t *dip, void *arg) 698 { 699 int pm_system_idle_threshold = *((int *)arg); 700 pm_info_t *info = PM_GET_PM_INFO(dip); 701 int processed = 0; 702 703 if (!info) 704 return (DDI_WALK_CONTINUE); 705 706 if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) { 707 switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) { 708 case PMC_DEF_THRESH: 709 PMD(PMD_IOCTL, ("ioctl: set_sys_threshold: set " 710 "%s@%s(%s#%d) default thresh to 0t%d\n", 711 PM_DEVICE(dip), pm_system_idle_threshold)) 712 pm_set_device_threshold(dip, pm_system_idle_threshold, 713 PMC_DEF_THRESH); 714 processed++; 715 break; 716 default: 717 break; 718 } 719 } 720 721 if (processed && autopm_enabled) 722 pm_rescan(dip); 723 724 return (DDI_WALK_CONTINUE); 725 } 726 727 /*ARGSUSED*/ 728 static int 729 pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 730 { 731 dev_t dev; 732 int instance; 733 734 switch (infocmd) { 735 case DDI_INFO_DEVT2DEVINFO: 736 if (pmstp->pm_instance == -1) 737 return (DDI_FAILURE); 738 *result = pmstp->pm_dip; 739 return (DDI_SUCCESS); 740 741 case DDI_INFO_DEVT2INSTANCE: 742 dev = (dev_t)arg; 743 instance = getminor(dev) >> 8; 744 *result = (void *)(uintptr_t)instance; 745 return (DDI_SUCCESS); 746 747 default: 748 return (DDI_FAILURE); 749 } 750 } 751 752 753 /*ARGSUSED1*/ 754 static int 755 pm_open(dev_t *devp, int flag, int otyp, cred_t *cr) 756 { 757 int clone; 758 759 if (otyp != OTYP_CHR) 760 return (EINVAL); 761 762 mutex_enter(&pm_clone_lock); 763 for (clone = 1; clone < PM_MAX_CLONE; clone++) 764 if (!pmstp->pm_clones[clone]) 765 break; 766 767 if (clone == PM_MAX_CLONE) { 768 mutex_exit(&pm_clone_lock); 769 return (ENXIO); 770 } 771 pmstp->pm_cred[clone] = cr; 772 crhold(cr); 773 774 *devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone); 775 pmstp->pm_clones[clone] = 1; 776 mutex_exit(&pm_clone_lock); 777 778 return (0); 779 } 780 781 /*ARGSUSED1*/ 782 static int 783 pm_close(dev_t dev, int flag, int otyp, cred_t *cr) 784 { 785 int clone; 786 787 if (otyp != OTYP_CHR) 788 return (EINVAL); 789 790 clone = PM_MINOR_TO_CLONE(getminor(dev)); 791 PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev), 792 clone)) 793 794 /* 795 * Walk the entire device tree to find the corresponding 796 * device and operate on it. 797 */ 798 ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device, 799 (void *) &clone); 800 801 crfree(pmstp->pm_cred[clone]); 802 pmstp->pm_cred[clone] = 0; 803 pmstp->pm_clones[clone] = 0; 804 pm_discard_entries(clone); 805 ASSERT(pm_poll_cnt[clone] == 0); 806 pm_deregister_watcher(clone, NULL); 807 return (0); 808 } 809 810 /*ARGSUSED*/ 811 static int 812 pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) 813 { 814 struct pm_cmd_info *pc_info(int); 815 struct pm_cmd_info *pcip = pc_info(cmd); 816 pm_req_t req; 817 dev_info_t *dip = NULL; 818 pm_info_t *info = NULL; 819 int clone; 820 char *cmdstr = pm_decode_cmd(cmd); 821 /* 822 * To keep devinfo nodes from going away while we're holding a 823 * pointer to their dip, pm_name_to_dip() optionally holds 824 * the devinfo node. If we've done that, we set dipheld 825 * so we know at the end of the ioctl processing to release the 826 * node again. 827 */ 828 int dipheld = 0; 829 int icount = 0; 830 int i; 831 int comps; 832 size_t lencopied; 833 int ret = ENOTTY; 834 int curpower; 835 char who[MAXNAMELEN]; 836 size_t wholen; /* copyinstr length */ 837 size_t deplen = MAXNAMELEN; 838 char *dep, i_dep_buf[MAXNAMELEN]; 839 char *pathbuf; 840 struct pm_component *cp; 841 #ifdef _MULTI_DATAMODEL 842 pm_state_change32_t *pscp32; 843 pm_state_change32_t psc32; 844 size_t copysize32; 845 #endif 846 pm_state_change_t *pscp; 847 pm_state_change_t psc; 848 size_t copysize; 849 extern int pm_default_idle_threshold; 850 extern int pm_system_idle_threshold; 851 extern void pm_record_thresh(pm_thresh_rec_t *); 852 psce_t *pm_psc_clone_to_direct(int); 853 psce_t *pm_psc_clone_to_interest(int); 854 extern void pm_register_watcher(int, dev_info_t *); 855 extern int pm_get_current_power(dev_info_t *, int, int *); 856 extern int pm_interest_registered(int); 857 extern void pm_all_to_default_thresholds(void); 858 extern int pm_current_threshold(dev_info_t *, int, int *); 859 extern void pm_deregister_watcher(int, dev_info_t *); 860 extern void pm_unrecord_threshold(char *); 861 862 PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr)) 863 864 #ifdef DEBUG 865 if (cmd == 666) { 866 ddi_walk_devs(ddi_root_node(), print_info, NULL); 867 return (0); 868 } 869 ret = 0x0badcafe; /* sanity checking */ 870 pm_cmd = cmd; /* for ASSERT debugging */ 871 pm_cmd_string = cmdstr; /* for ASSERT debugging */ 872 #endif 873 874 875 if (pcip == NULL) { 876 PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd)) 877 return (ENOTTY); 878 } 879 if (pcip == NULL || pcip->supported == 0) { 880 PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n", 881 pcip->name)) 882 return (ENOTTY); 883 } 884 885 wholen = 0; 886 dep = i_dep_buf; 887 i_dep_buf[0] = 0; 888 clone = PM_MINOR_TO_CLONE(getminor(dev)); 889 if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) { 890 ret = EPERM; 891 return (ret); 892 } 893 switch (pcip->str_type) { 894 case PM_REQ: 895 #ifdef _MULTI_DATAMODEL 896 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 897 pm_req32_t req32; 898 899 if (ddi_copyin((caddr_t)arg, &req32, 900 sizeof (req32), mode) != 0) { 901 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " 902 "EFAULT\n\n", cmdstr)) 903 ret = EFAULT; 904 break; 905 } 906 req.component = req32.component; 907 req.value = req32.value; 908 req.datasize = req32.datasize; 909 if (pcip->inargs & INWHO) { 910 ret = copyinstr((char *)(uintptr_t) 911 req32.physpath, who, MAXNAMELEN, &wholen); 912 if (ret) { 913 PMD(PMD_ERROR, ("ioctl: %s: " 914 "copyinstr fails returning %d\n", 915 cmdstr, ret)) 916 break; 917 } 918 req.physpath = who; 919 } 920 PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr, 921 req.physpath)) 922 if (pcip->inargs & INDATA) { 923 req.data = (void *)(uintptr_t)req32.data; 924 req.datasize = req32.datasize; 925 } else { 926 req.data = NULL; 927 req.datasize = 0; 928 } 929 switch (pcip->diptype) { 930 case DIP: 931 if (!(dip = 932 pm_name_to_dip(req.physpath, 1))) { 933 PMD(PMD_ERROR, ("ioctl: %s: " 934 "pm_name_to_dip for %s failed\n", 935 cmdstr, req.physpath)) 936 return (ENODEV); 937 } 938 ASSERT(!dipheld); 939 dipheld++; 940 break; 941 case NODIP: 942 break; 943 default: 944 /* 945 * Internal error, invalid ioctl description 946 * force debug entry even if pm_debug not set 947 */ 948 #ifdef DEBUG 949 pm_log("invalid diptype %d for cmd %d (%s)\n", 950 pcip->diptype, cmd, pcip->name); 951 #endif 952 ASSERT(0); 953 return (EIO); 954 } 955 if (pcip->inargs & INDATAINT) { 956 int32_t int32buf; 957 int32_t *i32p; 958 int *ip; 959 icount = req32.datasize / sizeof (int32_t); 960 if (icount <= 0) { 961 PMD(PMD_ERROR, ("ioctl: %s: datasize" 962 " 0 or neg EFAULT\n\n", cmdstr)) 963 ret = EFAULT; 964 break; 965 } 966 ASSERT(!(pcip->inargs & INDATASTRING)); 967 req.datasize = icount * sizeof (int); 968 req.data = kmem_alloc(req.datasize, KM_SLEEP); 969 ip = req.data; 970 ret = 0; 971 for (i = 0, 972 i32p = (int32_t *)(uintptr_t)req32.data; 973 i < icount; i++, i32p++) { 974 if (ddi_copyin((void *)i32p, &int32buf, 975 sizeof (int32_t), mode)) { 976 kmem_free(req.data, 977 req.datasize); 978 PMD(PMD_ERROR, ("ioctl: %s: " 979 "entry %d EFAULT\n", 980 cmdstr, i)) 981 ret = EFAULT; 982 break; 983 } 984 *ip++ = (int)int32buf; 985 } 986 if (ret) 987 break; 988 } 989 if (pcip->inargs & INDATASTRING) { 990 ASSERT(!(pcip->inargs & INDATAINT)); 991 ASSERT(pcip->deptype == DEP); 992 if (req32.data != NULL) { 993 size_t dummy; 994 if (copyinstr((void *)(uintptr_t) 995 req32.data, dep, deplen, &dummy)) { 996 PMD(PMD_ERROR, ("ioctl: %s: " 997 "0x%p dep size %lx, EFAULT" 998 "\n", cmdstr, 999 (void *)req.data, deplen)) 1000 ret = EFAULT; 1001 break; 1002 } 1003 #ifdef DEBUG 1004 else { 1005 PMD(PMD_DEP, ("ioctl: %s: " 1006 "dep %s\n", cmdstr, dep)) 1007 } 1008 #endif 1009 } else { 1010 PMD(PMD_ERROR, ("ioctl: %s: no " 1011 "dependent\n", cmdstr)) 1012 ret = EINVAL; 1013 break; 1014 } 1015 } 1016 } else 1017 #endif /* _MULTI_DATAMODEL */ 1018 { 1019 if (ddi_copyin((caddr_t)arg, 1020 &req, sizeof (req), mode) != 0) { 1021 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " 1022 "EFAULT\n\n", cmdstr)) 1023 ret = EFAULT; 1024 break; 1025 } 1026 if (pcip->inargs & INWHO) { 1027 ret = copyinstr((char *)req.physpath, who, 1028 MAXNAMELEN, &wholen); 1029 if (ret) { 1030 PMD(PMD_ERROR, ("ioctl: %s copyinstr" 1031 " fails returning %d\n", cmdstr, 1032 ret)) 1033 break; 1034 } 1035 req.physpath = who; 1036 } 1037 PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr, 1038 req.physpath)) 1039 if (!(pcip->inargs & INDATA)) { 1040 req.data = NULL; 1041 req.datasize = 0; 1042 } 1043 switch (pcip->diptype) { 1044 case DIP: 1045 if (!(dip = 1046 pm_name_to_dip(req.physpath, 1))) { 1047 PMD(PMD_ERROR, ("ioctl: %s: " 1048 "pm_name_to_dip for %s failed\n", 1049 cmdstr, req.physpath)) 1050 return (ENODEV); 1051 } 1052 ASSERT(!dipheld); 1053 dipheld++; 1054 break; 1055 case NODIP: 1056 break; 1057 default: 1058 /* 1059 * Internal error, invalid ioctl description 1060 * force debug entry even if pm_debug not set 1061 */ 1062 #ifdef DEBUG 1063 pm_log("invalid diptype %d for cmd %d (%s)\n", 1064 pcip->diptype, cmd, pcip->name); 1065 #endif 1066 ASSERT(0); 1067 return (EIO); 1068 } 1069 if (pcip->inargs & INDATAINT) { 1070 int *ip; 1071 1072 ASSERT(!(pcip->inargs & INDATASTRING)); 1073 ip = req.data; 1074 icount = req.datasize / sizeof (int); 1075 if (icount <= 0) { 1076 PMD(PMD_ERROR, ("ioctl: %s: datasize" 1077 " 0 or neg EFAULT\n\n", cmdstr)) 1078 ret = EFAULT; 1079 break; 1080 } 1081 req.data = kmem_alloc(req.datasize, KM_SLEEP); 1082 if (ddi_copyin((caddr_t)ip, req.data, 1083 req.datasize, mode) != 0) { 1084 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " 1085 "EFAULT\n\n", cmdstr)) 1086 ret = EFAULT; 1087 break; 1088 } 1089 } 1090 if (pcip->inargs & INDATASTRING) { 1091 ASSERT(!(pcip->inargs & INDATAINT)); 1092 ASSERT(pcip->deptype == DEP); 1093 if (req.data != NULL) { 1094 size_t dummy; 1095 if (copyinstr((caddr_t)req.data, 1096 dep, deplen, &dummy)) { 1097 PMD(PMD_ERROR, ("ioctl: %s: " 1098 "0x%p dep size %lu, " 1099 "EFAULT\n", cmdstr, 1100 (void *)req.data, deplen)) 1101 ret = EFAULT; 1102 break; 1103 } 1104 #ifdef DEBUG 1105 else { 1106 PMD(PMD_DEP, ("ioctl: %s: " 1107 "dep %s\n", cmdstr, dep)) 1108 } 1109 #endif 1110 } else { 1111 PMD(PMD_ERROR, ("ioctl: %s: no " 1112 "dependent\n", cmdstr)) 1113 ret = EINVAL; 1114 break; 1115 } 1116 } 1117 } 1118 /* 1119 * Now we've got all the args in for the commands that 1120 * use the new pm_req struct. 1121 */ 1122 switch (cmd) { 1123 case PM_REPARSE_PM_PROPS: 1124 { 1125 struct dev_ops *drv; 1126 struct cb_ops *cb; 1127 void *propval; 1128 int length; 1129 /* 1130 * This ioctl is provided only for the ddivs pm test. 1131 * We only do it to a driver which explicitly allows 1132 * us to do so by exporting a pm-reparse-ok property. 1133 * We only care whether the property exists or not. 1134 */ 1135 if ((drv = ddi_get_driver(dip)) == NULL) { 1136 ret = EINVAL; 1137 break; 1138 } 1139 if ((cb = drv->devo_cb_ops) != NULL) { 1140 if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip, 1141 PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP | 1142 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 1143 "pm-reparse-ok", (caddr_t)&propval, 1144 &length) != DDI_SUCCESS) { 1145 ret = EINVAL; 1146 break; 1147 } 1148 } else if (ddi_prop_op(DDI_DEV_T_ANY, dip, 1149 PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP | 1150 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 1151 "pm-reparse-ok", (caddr_t)&propval, 1152 &length) != DDI_SUCCESS) { 1153 ret = EINVAL; 1154 break; 1155 } 1156 kmem_free(propval, length); 1157 ret = e_new_pm_props(dip); 1158 break; 1159 } 1160 1161 case PM_GET_DEVICE_THRESHOLD: 1162 PM_LOCK_DIP(dip); 1163 if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) { 1164 PM_UNLOCK_DIP(dip); 1165 PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n", 1166 cmdstr)) 1167 ret = ENODEV; 1168 break; 1169 } 1170 *rval_p = DEVI(dip)->devi_pm_dev_thresh; 1171 PM_UNLOCK_DIP(dip); 1172 ret = 0; 1173 break; 1174 1175 case PM_DIRECT_PM: 1176 { 1177 int has_dep; 1178 if ((info = PM_GET_PM_INFO(dip)) == NULL) { 1179 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1180 "ENODEV\n", cmdstr)) 1181 ret = ENODEV; 1182 break; 1183 } 1184 /* 1185 * Check to see if we are there is a dependency on 1186 * this kept device, if so, return EBUSY. 1187 */ 1188 pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 1189 (void) ddi_pathname(dip, pathbuf); 1190 pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT, 1191 NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0); 1192 kmem_free(pathbuf, MAXPATHLEN); 1193 if (has_dep) { 1194 PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n", 1195 cmdstr)) 1196 ret = EBUSY; 1197 break; 1198 } 1199 PM_LOCK_DIP(dip); 1200 if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) { 1201 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1202 "%s@%s(%s#%d): EBUSY\n", cmdstr, 1203 PM_DEVICE(dip))) 1204 PM_UNLOCK_DIP(dip); 1205 ret = EBUSY; 1206 break; 1207 } 1208 info->pmi_dev_pm_state |= PM_DIRECT; 1209 info->pmi_clone = clone; 1210 PM_UNLOCK_DIP(dip); 1211 PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n", 1212 cmdstr, (void *)info, clone)) 1213 mutex_enter(&pm_clone_lock); 1214 pm_register_watcher(clone, dip); 1215 mutex_exit(&pm_clone_lock); 1216 ret = 0; 1217 break; 1218 } 1219 1220 case PM_RELEASE_DIRECT_PM: 1221 { 1222 if ((info = PM_GET_PM_INFO(dip)) == NULL) { 1223 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1224 "ENODEV\n", cmdstr)) 1225 ret = ENODEV; 1226 break; 1227 } 1228 PM_LOCK_DIP(dip); 1229 if (info->pmi_clone != clone) { 1230 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1231 "%s@%s(%s#%d) EINVAL\n", cmdstr, 1232 PM_DEVICE(dip))) 1233 ret = EINVAL; 1234 PM_UNLOCK_DIP(dip); 1235 break; 1236 } 1237 ASSERT(PM_ISDIRECT(dip)); 1238 info->pmi_dev_pm_state &= ~PM_DIRECT; 1239 PM_UNLOCK_DIP(dip); 1240 /* Bring ourselves up if there is a keeper. */ 1241 pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 1242 (void) ddi_pathname(dip, pathbuf); 1243 pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, 1244 NULL, pathbuf, PM_DEP_WAIT, NULL, 0); 1245 kmem_free(pathbuf, MAXPATHLEN); 1246 pm_discard_entries(clone); 1247 pm_deregister_watcher(clone, dip); 1248 /* 1249 * Now we could let the other threads that are 1250 * trying to do a DIRECT_PM thru 1251 */ 1252 PM_LOCK_DIP(dip); 1253 info->pmi_clone = 0; 1254 PM_UNLOCK_DIP(dip); 1255 pm_proceed(dip, PMP_RELEASE, -1, -1); 1256 PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n", 1257 cmdstr)) 1258 pm_rescan(dip); 1259 ret = 0; 1260 break; 1261 } 1262 1263 case PM_SET_CURRENT_POWER: 1264 { 1265 int comp = req.component; 1266 int value = req.value; 1267 PMD(PMD_DPM, ("ioctl: %s: %s component %d to value " 1268 "%d\n", cmdstr, req.physpath, comp, value)) 1269 if (!e_pm_valid_comp(dip, comp, NULL) || 1270 !e_pm_valid_power(dip, comp, value)) { 1271 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1272 "physpath=%s, comp=%d, level=%d, fails\n", 1273 cmdstr, req.physpath, comp, value)) 1274 ret = EINVAL; 1275 break; 1276 } 1277 1278 if ((info = PM_GET_PM_INFO(dip)) == NULL) { 1279 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1280 "ENODEV\n", cmdstr)) 1281 ret = ENODEV; 1282 break; 1283 } 1284 if (info->pmi_clone != clone) { 1285 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1286 "(not owner) %s fails; clone %d, owner %d" 1287 "\n", cmdstr, req.physpath, clone, 1288 info->pmi_clone)) 1289 ret = EINVAL; 1290 break; 1291 } 1292 ASSERT(PM_ISDIRECT(dip)); 1293 1294 if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT, 1295 PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) { 1296 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: " 1297 "pm_set_power for %s fails, errno=%d\n", 1298 cmdstr, req.physpath, ret)) 1299 break; 1300 } 1301 1302 pm_proceed(dip, PMP_SETPOWER, comp, value); 1303 1304 /* 1305 * Power down all idle components if console framebuffer 1306 * is powered off. 1307 */ 1308 if (PM_IS_CFB(dip) && (pm_system_idle_threshold == 1309 pm_default_idle_threshold)) { 1310 dev_info_t *root = ddi_root_node(); 1311 if (PM_ISBC(dip)) { 1312 if (comp == 0 && value == 0 && 1313 (pm_timeout_idledown() != 0)) { 1314 ddi_walk_devs(root, 1315 pm_start_idledown, 1316 (void *)PMID_CFB); 1317 } 1318 } else { 1319 int count = 0; 1320 for (i = 0; i < PM_NUMCMPTS(dip); i++) { 1321 ret = pm_get_current_power(dip, 1322 i, &curpower); 1323 if (ret == DDI_SUCCESS && 1324 curpower == 0) 1325 count++; 1326 } 1327 if ((count == PM_NUMCMPTS(dip)) && 1328 (pm_timeout_idledown() != 0)) { 1329 ddi_walk_devs(root, 1330 pm_start_idledown, 1331 (void *)PMID_CFB); 1332 } 1333 } 1334 } 1335 1336 PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n", 1337 cmdstr)) 1338 pm_rescan(dip); 1339 *rval_p = 0; 1340 ret = 0; 1341 break; 1342 } 1343 1344 case PM_GET_FULL_POWER: 1345 { 1346 int normal; 1347 ASSERT(dip); 1348 PMD(PMD_NORM, ("ioctl: %s: %s component %d\n", 1349 cmdstr, req.physpath, req.component)) 1350 normal = pm_get_normal_power(dip, req.component); 1351 1352 if (normal == DDI_FAILURE) { 1353 PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: " 1354 "returns EINVAL\n", cmdstr)) 1355 ret = EINVAL; 1356 break; 1357 } 1358 *rval_p = normal; 1359 PMD(PMD_NORM, ("ioctl: %s: returns %d\n", 1360 cmdstr, normal)) 1361 ret = 0; 1362 break; 1363 } 1364 1365 case PM_GET_CURRENT_POWER: 1366 if (pm_get_current_power(dip, req.component, 1367 rval_p) != DDI_SUCCESS) { 1368 PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s " 1369 "EINVAL\n", cmdstr)) 1370 ret = EINVAL; 1371 break; 1372 } 1373 PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n", 1374 cmdstr, req.physpath, req.component, *rval_p)) 1375 if (*rval_p == PM_LEVEL_UNKNOWN) 1376 ret = EAGAIN; 1377 else 1378 ret = 0; 1379 break; 1380 1381 case PM_GET_TIME_IDLE: 1382 { 1383 time_t timestamp; 1384 int comp = req.component; 1385 pm_component_t *cp; 1386 if (!e_pm_valid_comp(dip, comp, &cp)) { 1387 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1388 "component %d > numcmpts - 1 %d--EINVAL\n", 1389 cmdstr, PM_DEVICE(dip), comp, 1390 PM_NUMCMPTS(dip) - 1)) 1391 ret = EINVAL; 1392 break; 1393 } 1394 timestamp = cp->pmc_timestamp; 1395 if (timestamp) { 1396 time_t now; 1397 (void) drv_getparm(TIME, &now); 1398 *rval_p = (now - timestamp); 1399 } else { 1400 *rval_p = 0; 1401 } 1402 ret = 0; 1403 break; 1404 } 1405 1406 case PM_ADD_DEPENDENT: 1407 { 1408 dev_info_t *kept_dip; 1409 1410 PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr, 1411 dep, req.physpath)) 1412 1413 /* 1414 * hold and install kept while processing dependency 1415 * keeper (in .physpath) has already been held. 1416 */ 1417 if (dep[0] == '\0') { 1418 PMD(PMD_ERROR, ("kept NULL or null\n")) 1419 ret = EINVAL; 1420 break; 1421 } else if ((kept_dip = 1422 pm_name_to_dip(dep, 1)) == NULL) { 1423 PMD(PMD_ERROR, ("no dip for kept %s\n", dep)) 1424 ret = ENODEV; 1425 break; 1426 } else if (kept_dip == dip) { 1427 PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) " 1428 "self-dependency not allowed.\n", 1429 dep, (void *)kept_dip, req.physpath, 1430 (void *) dip)) 1431 PM_RELE(dip); /* release "double" hold */ 1432 ret = EINVAL; 1433 break; 1434 } 1435 ASSERT(!(strcmp(req.physpath, (char *)dep) == 0)); 1436 1437 /* 1438 * record dependency, then walk through device tree 1439 * independently on behalf of kept and keeper to 1440 * establish newly created dependency. 1441 */ 1442 pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER, 1443 req.physpath, dep, PM_DEP_WAIT, NULL, 0); 1444 1445 /* 1446 * release kept after establishing dependency, keeper 1447 * is released as part of ioctl exit processing. 1448 */ 1449 PM_RELE(kept_dip); 1450 *rval_p = 0; 1451 ret = 0; 1452 break; 1453 } 1454 1455 case PM_ADD_DEPENDENT_PROPERTY: 1456 { 1457 char *keeper, *kept; 1458 1459 if (dep[0] == '\0') { 1460 PMD(PMD_ERROR, ("ioctl: %s: dep NULL or " 1461 "null\n", cmdstr)) 1462 ret = EINVAL; 1463 break; 1464 } 1465 kept = dep; 1466 keeper = req.physpath; 1467 /* 1468 * record keeper - kept dependency, then walk through 1469 * device tree to find out all attached keeper, walk 1470 * through again to apply dependency to all the 1471 * potential kept. 1472 */ 1473 pm_dispatch_to_dep_thread( 1474 PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept, 1475 PM_DEP_WAIT, NULL, 0); 1476 1477 *rval_p = 0; 1478 ret = 0; 1479 break; 1480 } 1481 1482 case PM_SET_DEVICE_THRESHOLD: 1483 { 1484 pm_thresh_rec_t *rp; 1485 pm_pte_t *ep; /* threshold header storage */ 1486 int *tp; /* threshold storage */ 1487 size_t size; 1488 extern int pm_thresh_specd(dev_info_t *); 1489 1490 /* 1491 * The header struct plus one entry struct plus one 1492 * threshold plus the length of the string 1493 */ 1494 size = sizeof (pm_thresh_rec_t) + 1495 (sizeof (pm_pte_t) * 1) + 1496 (1 * sizeof (int)) + 1497 strlen(req.physpath) + 1; 1498 1499 rp = kmem_zalloc(size, KM_SLEEP); 1500 rp->ptr_size = size; 1501 rp->ptr_numcomps = 0; /* means device threshold */ 1502 ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp)); 1503 rp->ptr_entries = ep; 1504 tp = (int *)((intptr_t)ep + 1505 (1 * sizeof (pm_pte_t))); 1506 ep->pte_numthresh = 1; 1507 ep->pte_thresh = tp; 1508 *tp++ = req.value; 1509 (void) strcat((char *)tp, req.physpath); 1510 rp->ptr_physpath = (char *)tp; 1511 ASSERT((intptr_t)tp + strlen(req.physpath) + 1 == 1512 (intptr_t)rp + rp->ptr_size); 1513 PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for " 1514 "%s\n", cmdstr, req.value, req.physpath)) 1515 pm_record_thresh(rp); 1516 /* 1517 * Don't free rp, pm_record_thresh() keeps it. 1518 * We don't try to apply it ourselves because we'd need 1519 * to know too much about locking. Since we don't 1520 * hold a lock the entry could be removed before 1521 * we get here 1522 */ 1523 ASSERT(dip == NULL); 1524 ret = 0; /* can't fail now */ 1525 if (!(dip = pm_name_to_dip(req.physpath, 1))) { 1526 break; 1527 } 1528 (void) pm_thresh_specd(dip); 1529 PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n", 1530 cmdstr, PM_DEVICE(dip))) 1531 PM_RELE(dip); 1532 break; 1533 } 1534 1535 case PM_RESET_DEVICE_THRESHOLD: 1536 { 1537 /* 1538 * This only applies to a currently attached and power 1539 * managed node 1540 */ 1541 /* 1542 * We don't do this to old-style drivers 1543 */ 1544 info = PM_GET_PM_INFO(dip); 1545 if (info == NULL) { 1546 PMD(PMD_ERROR, ("ioctl: %s: %s not power " 1547 "managed\n", cmdstr, req.physpath)) 1548 ret = EINVAL; 1549 break; 1550 } 1551 if (PM_ISBC(dip)) { 1552 PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n", 1553 cmdstr, req.physpath)) 1554 ret = EINVAL; 1555 break; 1556 } 1557 pm_unrecord_threshold(req.physpath); 1558 pm_set_device_threshold(dip, pm_system_idle_threshold, 1559 PMC_DEF_THRESH); 1560 ret = 0; 1561 break; 1562 } 1563 1564 case PM_GET_NUM_COMPONENTS: 1565 ret = 0; 1566 *rval_p = PM_NUMCMPTS(dip); 1567 break; 1568 1569 case PM_GET_DEVICE_TYPE: 1570 ret = 0; 1571 if ((info = PM_GET_PM_INFO(dip)) == NULL) { 1572 PMD(PMD_ERROR, ("ioctl: %s: " 1573 "PM_NO_PM_COMPONENTS\n", cmdstr)) 1574 *rval_p = PM_NO_PM_COMPONENTS; 1575 break; 1576 } 1577 if (PM_ISBC(dip)) { 1578 *rval_p = PM_CREATE_COMPONENTS; 1579 } else { 1580 *rval_p = PM_AUTOPM; 1581 } 1582 break; 1583 1584 case PM_SET_COMPONENT_THRESHOLDS: 1585 { 1586 int comps = 0; 1587 int *end = (int *)req.data + icount; 1588 pm_thresh_rec_t *rp; 1589 pm_pte_t *ep; /* threshold header storage */ 1590 int *tp; /* threshold storage */ 1591 int *ip; 1592 int j; 1593 size_t size; 1594 extern int pm_thresh_specd(dev_info_t *); 1595 extern int pm_valid_thresh(dev_info_t *, 1596 pm_thresh_rec_t *); 1597 1598 for (ip = req.data; *ip; ip++) { 1599 if (ip >= end) { 1600 ret = EFAULT; 1601 break; 1602 } 1603 comps++; 1604 /* skip over indicated number of entries */ 1605 for (j = *ip; j; j--) { 1606 if (++ip >= end) { 1607 ret = EFAULT; 1608 break; 1609 } 1610 } 1611 if (ret) 1612 break; 1613 } 1614 if (ret) 1615 break; 1616 if ((intptr_t)ip != (intptr_t)end - sizeof (int)) { 1617 /* did not exactly fill buffer */ 1618 ret = EINVAL; 1619 break; 1620 } 1621 if (comps == 0) { 1622 PMD(PMD_ERROR, ("ioctl: %s: %s 0 components" 1623 "--EINVAL\n", cmdstr, req.physpath)) 1624 ret = EINVAL; 1625 break; 1626 } 1627 /* 1628 * The header struct plus one entry struct per component 1629 * plus the size of the lists minus the counts 1630 * plus the length of the string 1631 */ 1632 size = sizeof (pm_thresh_rec_t) + 1633 (sizeof (pm_pte_t) * comps) + req.datasize - 1634 ((comps + 1) * sizeof (int)) + 1635 strlen(req.physpath) + 1; 1636 1637 rp = kmem_zalloc(size, KM_SLEEP); 1638 rp->ptr_size = size; 1639 rp->ptr_numcomps = comps; 1640 ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp)); 1641 rp->ptr_entries = ep; 1642 tp = (int *)((intptr_t)ep + 1643 (comps * sizeof (pm_pte_t))); 1644 for (ip = req.data; *ip; ep++) { 1645 ep->pte_numthresh = *ip; 1646 ep->pte_thresh = tp; 1647 for (j = *ip++; j; j--) { 1648 *tp++ = *ip++; 1649 } 1650 } 1651 (void) strcat((char *)tp, req.physpath); 1652 rp->ptr_physpath = (char *)tp; 1653 ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int)); 1654 ASSERT((intptr_t)tp + strlen(req.physpath) + 1 == 1655 (intptr_t)rp + rp->ptr_size); 1656 1657 ASSERT(dip == NULL); 1658 /* 1659 * If this is not a currently power managed node, 1660 * then we can't check for validity of the thresholds 1661 */ 1662 if (!(dip = pm_name_to_dip(req.physpath, 1))) { 1663 /* don't free rp, pm_record_thresh uses it */ 1664 pm_record_thresh(rp); 1665 PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip " 1666 "for %s failed\n", cmdstr, req.physpath)) 1667 ret = 0; 1668 break; 1669 } 1670 ASSERT(!dipheld); 1671 dipheld++; 1672 1673 if (!pm_valid_thresh(dip, rp)) { 1674 PMD(PMD_ERROR, ("ioctl: %s: invalid thresh " 1675 "for %s@%s(%s#%d)\n", cmdstr, 1676 PM_DEVICE(dip))) 1677 kmem_free(rp, size); 1678 ret = EINVAL; 1679 break; 1680 } 1681 /* 1682 * We don't just apply it ourselves because we'd need 1683 * to know too much about locking. Since we don't 1684 * hold a lock the entry could be removed before 1685 * we get here 1686 */ 1687 pm_record_thresh(rp); 1688 (void) pm_thresh_specd(dip); 1689 ret = 0; 1690 break; 1691 } 1692 1693 case PM_GET_COMPONENT_THRESHOLDS: 1694 { 1695 int musthave; 1696 int numthresholds = 0; 1697 int wordsize; 1698 int numcomps; 1699 caddr_t uaddr = req.data; /* user address */ 1700 int val; /* int value to be copied out */ 1701 int32_t val32; /* int32 value to be copied out */ 1702 caddr_t vaddr; /* address to copyout from */ 1703 int j; 1704 1705 #ifdef _MULTI_DATAMODEL 1706 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 1707 wordsize = sizeof (int32_t); 1708 } else 1709 #endif /* _MULTI_DATAMODEL */ 1710 { 1711 wordsize = sizeof (int); 1712 } 1713 1714 ASSERT(dip); 1715 1716 numcomps = PM_NUMCMPTS(dip); 1717 for (i = 0; i < numcomps; i++) { 1718 cp = PM_CP(dip, i); 1719 numthresholds += cp->pmc_comp.pmc_numlevels - 1; 1720 } 1721 musthave = (numthresholds + numcomps + 1) * wordsize; 1722 if (req.datasize < musthave) { 1723 PMD(PMD_ERROR, ("ioctl: %s: size %ld, need " 1724 "%d--EINVAL\n", cmdstr, req.datasize, 1725 musthave)) 1726 ret = EINVAL; 1727 break; 1728 } 1729 PM_LOCK_DIP(dip); 1730 for (i = 0; i < numcomps; i++) { 1731 int *thp; 1732 cp = PM_CP(dip, i); 1733 thp = cp->pmc_comp.pmc_thresh; 1734 /* first copyout the count */ 1735 if (wordsize == sizeof (int32_t)) { 1736 val32 = cp->pmc_comp.pmc_numlevels - 1; 1737 vaddr = (caddr_t)&val32; 1738 } else { 1739 val = cp->pmc_comp.pmc_numlevels - 1; 1740 vaddr = (caddr_t)&val; 1741 } 1742 if (ddi_copyout(vaddr, (void *)uaddr, 1743 wordsize, mode) != 0) { 1744 PM_UNLOCK_DIP(dip); 1745 PMD(PMD_ERROR, ("ioctl: %s: %s@%s" 1746 "(%s#%d) vaddr %p EFAULT\n", 1747 cmdstr, PM_DEVICE(dip), 1748 (void*)vaddr)) 1749 ret = EFAULT; 1750 break; 1751 } 1752 vaddr = uaddr; 1753 vaddr += wordsize; 1754 uaddr = (caddr_t)vaddr; 1755 /* then copyout each threshold value */ 1756 for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1; 1757 j++) { 1758 if (wordsize == sizeof (int32_t)) { 1759 val32 = thp[j + 1]; 1760 vaddr = (caddr_t)&val32; 1761 } else { 1762 val = thp[i + 1]; 1763 vaddr = (caddr_t)&val; 1764 } 1765 if (ddi_copyout(vaddr, (void *) uaddr, 1766 wordsize, mode) != 0) { 1767 PM_UNLOCK_DIP(dip); 1768 PMD(PMD_ERROR, ("ioctl: %s: " 1769 "%s@%s(%s#%d) uaddr %p " 1770 "EFAULT\n", cmdstr, 1771 PM_DEVICE(dip), 1772 (void *)uaddr)) 1773 ret = EFAULT; 1774 break; 1775 } 1776 vaddr = uaddr; 1777 vaddr += wordsize; 1778 uaddr = (caddr_t)vaddr; 1779 } 1780 } 1781 if (ret) 1782 break; 1783 /* last copyout a terminating 0 count */ 1784 if (wordsize == sizeof (int32_t)) { 1785 val32 = 0; 1786 vaddr = (caddr_t)&val32; 1787 } else { 1788 ASSERT(wordsize == sizeof (int)); 1789 val = 0; 1790 vaddr = (caddr_t)&val; 1791 } 1792 if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) { 1793 PM_UNLOCK_DIP(dip); 1794 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1795 "vaddr %p (0 count) EFAULT\n", cmdstr, 1796 PM_DEVICE(dip), (void *)vaddr)) 1797 ret = EFAULT; 1798 break; 1799 } 1800 /* finished, so don't need to increment addresses */ 1801 PM_UNLOCK_DIP(dip); 1802 ret = 0; 1803 break; 1804 } 1805 1806 case PM_GET_STATS: 1807 { 1808 time_t now; 1809 time_t *timestamp; 1810 extern int pm_cur_power(pm_component_t *cp); 1811 int musthave; 1812 int wordsize; 1813 1814 #ifdef _MULTI_DATAMODEL 1815 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 1816 wordsize = sizeof (int32_t); 1817 } else 1818 #endif /* _MULTI_DATAMODEL */ 1819 { 1820 wordsize = sizeof (int); 1821 } 1822 1823 comps = PM_NUMCMPTS(dip); 1824 if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) { 1825 PMD(PMD_ERROR, ("ioctl: %s: %s no components" 1826 " or not power managed--EINVAL\n", cmdstr, 1827 req.physpath)) 1828 ret = EINVAL; 1829 break; 1830 } 1831 musthave = comps * 2 * wordsize; 1832 if (req.datasize < musthave) { 1833 PMD(PMD_ERROR, ("ioctl: %s: size %lu, need " 1834 "%d--EINVAL\n", cmdstr, req.datasize, 1835 musthave)) 1836 ret = EINVAL; 1837 break; 1838 } 1839 1840 PM_LOCK_DIP(dip); 1841 (void) drv_getparm(TIME, &now); 1842 timestamp = kmem_zalloc(comps * sizeof (time_t), 1843 KM_SLEEP); 1844 pm_get_timestamps(dip, timestamp); 1845 /* 1846 * First the current power levels 1847 */ 1848 for (i = 0; i < comps; i++) { 1849 int curpwr; 1850 int32_t curpwr32; 1851 caddr_t cpaddr; 1852 1853 cp = PM_CP(dip, i); 1854 if (wordsize == sizeof (int)) { 1855 curpwr = pm_cur_power(cp); 1856 cpaddr = (caddr_t)&curpwr; 1857 } else { 1858 ASSERT(wordsize == sizeof (int32_t)); 1859 curpwr32 = pm_cur_power(cp); 1860 cpaddr = (caddr_t)&curpwr32; 1861 } 1862 if (ddi_copyout(cpaddr, (void *) req.data, 1863 wordsize, mode) != 0) { 1864 PM_UNLOCK_DIP(dip); 1865 PMD(PMD_ERROR, ("ioctl: %s: %s@%s" 1866 "(%s#%d) req.data %p EFAULT\n", 1867 cmdstr, PM_DEVICE(dip), 1868 (void *)req.data)) 1869 ASSERT(!dipheld); 1870 return (EFAULT); 1871 } 1872 cpaddr = (caddr_t)req.data; 1873 cpaddr += wordsize; 1874 req.data = cpaddr; 1875 } 1876 /* 1877 * Then the times remaining 1878 */ 1879 for (i = 0; i < comps; i++) { 1880 int retval; 1881 int32_t retval32; 1882 caddr_t rvaddr; 1883 int curpwr; 1884 1885 cp = PM_CP(dip, i); 1886 curpwr = cp->pmc_cur_pwr; 1887 if (curpwr == 0 || timestamp[i] == 0) { 1888 PMD(PMD_STATS, ("ioctl: %s: " 1889 "cur_pwer %x, timestamp %lx\n", 1890 cmdstr, curpwr, timestamp[i])) 1891 retval = INT_MAX; 1892 } else { 1893 int thresh; 1894 (void) pm_current_threshold(dip, i, 1895 &thresh); 1896 retval = thresh - (now - timestamp[i]); 1897 PMD(PMD_STATS, ("ioctl: %s: current " 1898 "thresh %x, now %lx, timestamp %lx," 1899 " retval %x\n", cmdstr, thresh, now, 1900 timestamp[i], retval)) 1901 } 1902 if (wordsize == sizeof (int)) { 1903 rvaddr = (caddr_t)&retval; 1904 } else { 1905 ASSERT(wordsize == sizeof (int32_t)); 1906 retval32 = retval; 1907 rvaddr = (caddr_t)&retval32; 1908 } 1909 if (ddi_copyout(rvaddr, (void *) req.data, 1910 wordsize, mode) != 0) { 1911 PM_UNLOCK_DIP(dip); 1912 PMD(PMD_ERROR, ("ioctl: %s: %s@%s" 1913 "(%s#%d) req.data %p EFAULT\n", 1914 cmdstr, PM_DEVICE(dip), 1915 (void *)req.data)) 1916 ASSERT(!dipheld); 1917 return (EFAULT); 1918 } 1919 rvaddr = (caddr_t)req.data; 1920 rvaddr += wordsize; 1921 req.data = (int *)rvaddr; 1922 } 1923 PM_UNLOCK_DIP(dip); 1924 *rval_p = comps; 1925 ret = 0; 1926 kmem_free(timestamp, comps * sizeof (time_t)); 1927 break; 1928 } 1929 1930 case PM_GET_COMPONENT_NAME: 1931 ASSERT(dip); 1932 if (!e_pm_valid_comp(dip, req.component, &cp)) { 1933 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1934 "component %d > numcmpts - 1 %d--EINVAL\n", 1935 cmdstr, PM_DEVICE(dip), req.component, 1936 PM_NUMCMPTS(dip) - 1)) 1937 ret = EINVAL; 1938 break; 1939 } 1940 if (ret = copyoutstr(cp->pmc_comp.pmc_name, 1941 (char *)req.data, req.datasize, &lencopied)) { 1942 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1943 "copyoutstr %p failed--EFAULT\n", cmdstr, 1944 PM_DEVICE(dip), (void *)req.data)) 1945 break; 1946 } 1947 *rval_p = lencopied; 1948 ret = 0; 1949 break; 1950 1951 case PM_GET_POWER_NAME: 1952 { 1953 int i; 1954 1955 ASSERT(dip); 1956 if (!e_pm_valid_comp(dip, req.component, &cp)) { 1957 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1958 "component %d > numcmpts - 1 %d--EINVAL\n", 1959 cmdstr, PM_DEVICE(dip), req.component, 1960 PM_NUMCMPTS(dip) - 1)) 1961 ret = EINVAL; 1962 break; 1963 } 1964 if ((i = req.value) < 0 || 1965 i > cp->pmc_comp.pmc_numlevels - 1) { 1966 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1967 "value %d > num_levels - 1 %d--EINVAL\n", 1968 cmdstr, PM_DEVICE(dip), req.value, 1969 cp->pmc_comp.pmc_numlevels - 1)) 1970 ret = EINVAL; 1971 break; 1972 } 1973 dep = cp->pmc_comp.pmc_lnames[req.value]; 1974 if (ret = copyoutstr(dep, 1975 req.data, req.datasize, &lencopied)) { 1976 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 1977 "copyoutstr %p failed--EFAULT\n", cmdstr, 1978 PM_DEVICE(dip), (void *)req.data)) 1979 break; 1980 } 1981 *rval_p = lencopied; 1982 ret = 0; 1983 break; 1984 } 1985 1986 case PM_GET_POWER_LEVELS: 1987 { 1988 int musthave; 1989 int numlevels; 1990 int wordsize; 1991 1992 #ifdef _MULTI_DATAMODEL 1993 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 1994 wordsize = sizeof (int32_t); 1995 } else 1996 #endif /* _MULTI_DATAMODEL */ 1997 { 1998 wordsize = sizeof (int); 1999 } 2000 ASSERT(dip); 2001 2002 if (!e_pm_valid_comp(dip, req.component, &cp)) { 2003 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 2004 "has %d components, component %d requested" 2005 "--EINVAL\n", cmdstr, PM_DEVICE(dip), 2006 PM_NUMCMPTS(dip), req.component)) 2007 ret = EINVAL; 2008 break; 2009 } 2010 numlevels = cp->pmc_comp.pmc_numlevels; 2011 musthave = numlevels * wordsize; 2012 if (req.datasize < musthave) { 2013 PMD(PMD_ERROR, ("ioctl: %s: size %lu, need " 2014 "%d--EINVAL\n", cmdstr, req.datasize, 2015 musthave)) 2016 ret = EINVAL; 2017 break; 2018 } 2019 PM_LOCK_DIP(dip); 2020 for (i = 0; i < numlevels; i++) { 2021 int level; 2022 int32_t level32; 2023 caddr_t laddr; 2024 2025 if (wordsize == sizeof (int)) { 2026 level = cp->pmc_comp.pmc_lvals[i]; 2027 laddr = (caddr_t)&level; 2028 } else { 2029 level32 = cp->pmc_comp.pmc_lvals[i]; 2030 laddr = (caddr_t)&level32; 2031 } 2032 if (ddi_copyout(laddr, (void *) req.data, 2033 wordsize, mode) != 0) { 2034 PM_UNLOCK_DIP(dip); 2035 PMD(PMD_ERROR, ("ioctl: %s: %s@%s" 2036 "(%s#%d) laddr %p EFAULT\n", 2037 cmdstr, PM_DEVICE(dip), 2038 (void *)laddr)) 2039 ASSERT(!dipheld); 2040 return (EFAULT); 2041 } 2042 laddr = (caddr_t)req.data; 2043 laddr += wordsize; 2044 req.data = (int *)laddr; 2045 } 2046 PM_UNLOCK_DIP(dip); 2047 *rval_p = numlevels; 2048 ret = 0; 2049 break; 2050 } 2051 2052 2053 case PM_GET_NUM_POWER_LEVELS: 2054 if (!e_pm_valid_comp(dip, req.component, &cp)) { 2055 PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " 2056 "component %d > numcmpts - 1 %d--EINVAL\n", 2057 cmdstr, PM_DEVICE(dip), req.component, 2058 PM_NUMCMPTS(dip) - 1)) 2059 ret = EINVAL; 2060 break; 2061 } 2062 *rval_p = cp->pmc_comp.pmc_numlevels; 2063 ret = 0; 2064 break; 2065 2066 case PM_GET_DEVICE_THRESHOLD_BASIS: 2067 ret = 0; 2068 PM_LOCK_DIP(dip); 2069 if ((info = PM_GET_PM_INFO(dip)) == NULL) { 2070 PM_UNLOCK_DIP(dip); 2071 PMD(PMD_ERROR, ("ioctl: %s: " 2072 "PM_NO_PM_COMPONENTS\n", cmdstr)) 2073 *rval_p = PM_NO_PM_COMPONENTS; 2074 break; 2075 } 2076 if (PM_ISDIRECT(dip)) { 2077 PM_UNLOCK_DIP(dip); 2078 *rval_p = PM_DIRECTLY_MANAGED; 2079 break; 2080 } 2081 switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) { 2082 case PMC_DEF_THRESH: 2083 case PMC_NEXDEF_THRESH: 2084 *rval_p = PM_DEFAULT_THRESHOLD; 2085 break; 2086 case PMC_DEV_THRESH: 2087 *rval_p = PM_DEVICE_THRESHOLD; 2088 break; 2089 case PMC_COMP_THRESH: 2090 *rval_p = PM_COMPONENT_THRESHOLD; 2091 break; 2092 default: 2093 if (PM_ISBC(dip)) { 2094 *rval_p = PM_OLD_THRESHOLD; 2095 break; 2096 } 2097 PMD(PMD_ERROR, ("ioctl: %s: default, not " 2098 "BC--EINVAL", cmdstr)) 2099 ret = EINVAL; 2100 break; 2101 } 2102 PM_UNLOCK_DIP(dip); 2103 break; 2104 } 2105 break; 2106 2107 case PM_PSC: 2108 /* 2109 * Commands that require pm_state_change_t as arg 2110 */ 2111 #ifdef _MULTI_DATAMODEL 2112 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 2113 pscp32 = (pm_state_change32_t *)arg; 2114 if (ddi_copyin((caddr_t)arg, &psc32, 2115 sizeof (psc32), mode) != 0) { 2116 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " 2117 "EFAULT\n\n", cmdstr)) 2118 ASSERT(!dipheld); 2119 return (EFAULT); 2120 } 2121 psc.physpath = (caddr_t)(uintptr_t)psc32.physpath; 2122 psc.size = psc32.size; 2123 } else 2124 #endif /* _MULTI_DATAMODEL */ 2125 { 2126 pscp = (pm_state_change_t *)arg; 2127 if (ddi_copyin((caddr_t)arg, &psc, 2128 sizeof (psc), mode) != 0) { 2129 PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " 2130 "EFAULT\n\n", cmdstr)) 2131 ASSERT(!dipheld); 2132 return (EFAULT); 2133 } 2134 } 2135 switch (cmd) { 2136 2137 case PM_GET_STATE_CHANGE: 2138 case PM_GET_STATE_CHANGE_WAIT: 2139 { 2140 psce_t *pscep; 2141 pm_state_change_t *p; 2142 caddr_t physpath; 2143 size_t physlen; 2144 2145 /* 2146 * We want to know if any device has changed state. 2147 * We look up by clone. In case we have another thread 2148 * from the same process, we loop. 2149 * pm_psc_clone_to_interest() returns a locked entry. 2150 * We create an internal copy of the event entry prior 2151 * to copyout to user space because we don't want to 2152 * hold the psce_lock while doing copyout as we might 2153 * hit page fault which eventually brings us back 2154 * here requesting the same lock. 2155 */ 2156 mutex_enter(&pm_clone_lock); 2157 if (!pm_interest_registered(clone)) 2158 pm_register_watcher(clone, NULL); 2159 while ((pscep = 2160 pm_psc_clone_to_interest(clone)) == NULL) { 2161 if (cmd == PM_GET_STATE_CHANGE) { 2162 PMD(PMD_IOCTL, ("ioctl: %s: " 2163 "EWOULDBLOCK\n", cmdstr)) 2164 mutex_exit(&pm_clone_lock); 2165 ASSERT(!dipheld); 2166 return (EWOULDBLOCK); 2167 } else { 2168 if (cv_wait_sig(&pm_clones_cv[clone], 2169 &pm_clone_lock) == 0) { 2170 mutex_exit(&pm_clone_lock); 2171 PMD(PMD_ERROR, ("ioctl: %s " 2172 "EINTR\n", cmdstr)) 2173 ASSERT(!dipheld); 2174 return (EINTR); 2175 } 2176 } 2177 } 2178 mutex_exit(&pm_clone_lock); 2179 2180 physlen = pscep->psce_out->size; 2181 physpath = NULL; 2182 /* 2183 * If we were unable to store the path while bringing 2184 * up the console fb upon entering the prom, we give 2185 * a "" name with the overrun event set 2186 */ 2187 if (physlen == (size_t)-1) { /* kmemalloc failed */ 2188 physpath = kmem_zalloc(1, KM_SLEEP); 2189 physlen = 1; 2190 } 2191 if ((psc.physpath == NULL) || (psc.size < physlen)) { 2192 PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr)) 2193 mutex_exit(&pscep->psce_lock); 2194 ret = EFAULT; 2195 break; 2196 } 2197 if (physpath == NULL) { 2198 physpath = kmem_zalloc(physlen, KM_SLEEP); 2199 bcopy((const void *) pscep->psce_out->physpath, 2200 (void *) physpath, physlen); 2201 } 2202 2203 p = pscep->psce_out; 2204 #ifdef _MULTI_DATAMODEL 2205 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 2206 #ifdef DEBUG 2207 size_t usrcopysize; 2208 #endif 2209 psc32.flags = (ushort_t)p->flags; 2210 psc32.event = (ushort_t)p->event; 2211 psc32.timestamp = (int32_t)p->timestamp; 2212 psc32.component = (int32_t)p->component; 2213 psc32.old_level = (int32_t)p->old_level; 2214 psc32.new_level = (int32_t)p->new_level; 2215 copysize32 = ((intptr_t)&psc32.size - 2216 (intptr_t)&psc32.component); 2217 #ifdef DEBUG 2218 usrcopysize = ((intptr_t)&pscp32->size - 2219 (intptr_t)&pscp32->component); 2220 ASSERT(usrcopysize == copysize32); 2221 #endif 2222 } else 2223 #endif /* _MULTI_DATAMODEL */ 2224 { 2225 psc.flags = p->flags; 2226 psc.event = p->event; 2227 psc.timestamp = p->timestamp; 2228 psc.component = p->component; 2229 psc.old_level = p->old_level; 2230 psc.new_level = p->new_level; 2231 copysize = ((long)&p->size - 2232 (long)&p->component); 2233 } 2234 if (p->size != (size_t)-1) 2235 kmem_free(p->physpath, p->size); 2236 p->size = 0; 2237 p->physpath = NULL; 2238 if (pscep->psce_out == pscep->psce_last) 2239 p = pscep->psce_first; 2240 else 2241 p++; 2242 pscep->psce_out = p; 2243 mutex_exit(&pscep->psce_lock); 2244 2245 ret = copyoutstr(physpath, psc.physpath, 2246 physlen, &lencopied); 2247 kmem_free(physpath, physlen); 2248 if (ret) { 2249 PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p " 2250 "failed--EFAULT\n", cmdstr, 2251 (void *)psc.physpath)) 2252 break; 2253 } 2254 2255 #ifdef _MULTI_DATAMODEL 2256 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 2257 if (ddi_copyout(&psc32.component, 2258 &pscp32->component, copysize32, mode) 2259 != 0) { 2260 PMD(PMD_ERROR, ("ioctl: %s: copyout " 2261 "failed--EFAULT\n", cmdstr)) 2262 ret = EFAULT; 2263 break; 2264 } 2265 } else 2266 #endif /* _MULTI_DATAMODEL */ 2267 { 2268 if (ddi_copyout(&psc.component, 2269 &pscp->component, copysize, mode) != 0) { 2270 PMD(PMD_ERROR, ("ioctl: %s: copyout " 2271 "failed--EFAULT\n", cmdstr)) 2272 ret = EFAULT; 2273 break; 2274 } 2275 } 2276 ret = 0; 2277 break; 2278 } 2279 2280 case PM_DIRECT_NOTIFY: 2281 case PM_DIRECT_NOTIFY_WAIT: 2282 { 2283 psce_t *pscep; 2284 pm_state_change_t *p; 2285 caddr_t physpath; 2286 size_t physlen; 2287 /* 2288 * We want to know if any direct device of ours has 2289 * something we should know about. We look up by clone. 2290 * In case we have another thread from the same process, 2291 * we loop. 2292 * pm_psc_clone_to_direct() returns a locked entry. 2293 */ 2294 mutex_enter(&pm_clone_lock); 2295 while (pm_poll_cnt[clone] == 0 || 2296 (pscep = pm_psc_clone_to_direct(clone)) == NULL) { 2297 if (cmd == PM_DIRECT_NOTIFY) { 2298 PMD(PMD_IOCTL, ("ioctl: %s: " 2299 "EWOULDBLOCK\n", cmdstr)) 2300 mutex_exit(&pm_clone_lock); 2301 ASSERT(!dipheld); 2302 return (EWOULDBLOCK); 2303 } else { 2304 if (cv_wait_sig(&pm_clones_cv[clone], 2305 &pm_clone_lock) == 0) { 2306 mutex_exit(&pm_clone_lock); 2307 PMD(PMD_ERROR, ("ioctl: %s: " 2308 "EINTR\n", cmdstr)) 2309 ASSERT(!dipheld); 2310 return (EINTR); 2311 } 2312 } 2313 } 2314 mutex_exit(&pm_clone_lock); 2315 physlen = pscep->psce_out->size; 2316 if ((psc.physpath == NULL) || (psc.size < physlen)) { 2317 mutex_exit(&pscep->psce_lock); 2318 PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", 2319 cmdstr)) 2320 ret = EFAULT; 2321 break; 2322 } 2323 physpath = kmem_zalloc(physlen, KM_SLEEP); 2324 bcopy((const void *) pscep->psce_out->physpath, 2325 (void *) physpath, physlen); 2326 2327 p = pscep->psce_out; 2328 #ifdef _MULTI_DATAMODEL 2329 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 2330 #ifdef DEBUG 2331 size_t usrcopysize; 2332 #endif 2333 psc32.component = (int32_t)p->component; 2334 psc32.flags = (ushort_t)p->flags; 2335 psc32.event = (ushort_t)p->event; 2336 psc32.timestamp = (int32_t)p->timestamp; 2337 psc32.old_level = (int32_t)p->old_level; 2338 psc32.new_level = (int32_t)p->new_level; 2339 copysize32 = (intptr_t)&psc32.size - 2340 (intptr_t)&psc32.component; 2341 PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d " 2342 "%d -> %d\n", cmdstr, physpath, 2343 p->component, p->old_level, p->new_level)) 2344 #ifdef DEBUG 2345 usrcopysize = (intptr_t)&pscp32->size - 2346 (intptr_t)&pscp32->component; 2347 ASSERT(usrcopysize == copysize32); 2348 #endif 2349 } else 2350 #endif 2351 { 2352 psc.component = p->component; 2353 psc.flags = p->flags; 2354 psc.event = p->event; 2355 psc.timestamp = p->timestamp; 2356 psc.old_level = p->old_level; 2357 psc.new_level = p->new_level; 2358 copysize = (intptr_t)&p->size - 2359 (intptr_t)&p->component; 2360 PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d " 2361 "%d -> %d\n", cmdstr, physpath, 2362 p->component, p->old_level, p->new_level)) 2363 } 2364 mutex_enter(&pm_clone_lock); 2365 PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d " 2366 "before decrement\n", cmdstr, clone, 2367 pm_poll_cnt[clone])) 2368 pm_poll_cnt[clone]--; 2369 mutex_exit(&pm_clone_lock); 2370 kmem_free(p->physpath, p->size); 2371 p->size = 0; 2372 p->physpath = NULL; 2373 if (pscep->psce_out == pscep->psce_last) 2374 p = pscep->psce_first; 2375 else 2376 p++; 2377 pscep->psce_out = p; 2378 mutex_exit(&pscep->psce_lock); 2379 2380 ret = copyoutstr(physpath, psc.physpath, 2381 physlen, &lencopied); 2382 kmem_free(physpath, physlen); 2383 if (ret) { 2384 PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p " 2385 "failed--EFAULT\n", cmdstr, 2386 (void *)psc.physpath)) 2387 break; 2388 } 2389 2390 #ifdef _MULTI_DATAMODEL 2391 if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { 2392 if (ddi_copyout(&psc32.component, 2393 &pscp32->component, copysize32, mode) 2394 != 0) { 2395 PMD(PMD_ERROR, ("ioctl: %s: copyout " 2396 "failed--EFAULT\n", cmdstr)) 2397 ret = EFAULT; 2398 break; 2399 } 2400 } else 2401 #endif /* _MULTI_DATAMODEL */ 2402 { 2403 if (ddi_copyout(&psc.component, 2404 &pscp->component, copysize, mode) != 0) { 2405 PMD(PMD_ERROR, ("ioctl: %s: copyout " 2406 "failed--EFAULT\n", cmdstr)) 2407 ret = EFAULT; 2408 break; 2409 } 2410 } 2411 ret = 0; 2412 break; 2413 } 2414 default: 2415 ASSERT(0); 2416 } 2417 break; 2418 2419 case NOSTRUCT: 2420 switch (cmd) { 2421 case PM_START_PM: 2422 mutex_enter(&pm_scan_lock); 2423 if (autopm_enabled) { 2424 mutex_exit(&pm_scan_lock); 2425 PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n", 2426 cmdstr)) 2427 ret = EBUSY; 2428 break; 2429 } 2430 autopm_enabled = 1; 2431 mutex_exit(&pm_scan_lock); 2432 ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd); 2433 ret = 0; 2434 break; 2435 2436 case PM_RESET_PM: 2437 case PM_STOP_PM: 2438 { 2439 extern void pm_discard_thresholds(void); 2440 2441 mutex_enter(&pm_scan_lock); 2442 if (!autopm_enabled && cmd != PM_RESET_PM) { 2443 mutex_exit(&pm_scan_lock); 2444 PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n", 2445 cmdstr)) 2446 ret = EINVAL; 2447 break; 2448 } 2449 autopm_enabled = 0; 2450 mutex_exit(&pm_scan_lock); 2451 /* 2452 * bring devices to full power level, stop scan 2453 */ 2454 ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd); 2455 ret = 0; 2456 if (cmd == PM_STOP_PM) 2457 break; 2458 /* 2459 * Now do only PM_RESET_PM stuff. 2460 */ 2461 pm_system_idle_threshold = pm_default_idle_threshold; 2462 pm_discard_thresholds(); 2463 pm_all_to_default_thresholds(); 2464 pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP, 2465 NULL, NULL, PM_DEP_WAIT, NULL, 0); 2466 break; 2467 } 2468 2469 case PM_GET_SYSTEM_THRESHOLD: 2470 *rval_p = pm_system_idle_threshold; 2471 ret = 0; 2472 break; 2473 2474 case PM_GET_DEFAULT_SYSTEM_THRESHOLD: 2475 *rval_p = pm_default_idle_threshold; 2476 ret = 0; 2477 break; 2478 2479 case PM_SET_SYSTEM_THRESHOLD: 2480 if ((int)arg < 0) { 2481 PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0" 2482 "--EINVAL\n", cmdstr, (int)arg)) 2483 ret = EINVAL; 2484 break; 2485 } 2486 PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr, 2487 (int)arg, (int)arg)) 2488 pm_system_idle_threshold = (int)arg; 2489 ddi_walk_devs(ddi_root_node(), pm_set_sys_threshold, 2490 (void *) &pm_system_idle_threshold); 2491 ret = 0; 2492 break; 2493 2494 case PM_IDLE_DOWN: 2495 if (pm_timeout_idledown() != 0) { 2496 ddi_walk_devs(ddi_root_node(), 2497 pm_start_idledown, (void *)PMID_IOC); 2498 } 2499 ret = 0; 2500 break; 2501 2502 case PM_GET_PM_STATE: 2503 if (autopm_enabled) { 2504 *rval_p = PM_SYSTEM_PM_ENABLED; 2505 } else { 2506 *rval_p = PM_SYSTEM_PM_DISABLED; 2507 } 2508 ret = 0; 2509 break; 2510 } 2511 break; 2512 2513 2514 default: 2515 /* 2516 * Internal error, invalid ioctl description 2517 * force debug entry even if pm_debug not set 2518 */ 2519 #ifdef DEBUG 2520 pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n", 2521 pcip->str_type, cmd, pcip->name); 2522 #endif 2523 ASSERT(0); 2524 return (EIO); 2525 } 2526 ASSERT(ret != 0x0badcafe); /* some cmd in wrong case! */ 2527 if (dipheld) { 2528 ASSERT(dip); 2529 PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for " 2530 "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip))) 2531 PM_RELE(dip); 2532 } 2533 PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret)) 2534 return (ret); 2535 } 2536