|
|||
Prex Home / Browse Source - Prex Version: 0.9.0 |
|||
root/bsp/drv/dev/base/pm.c/* [<][>][^][v][top][bottom][index][help] */DEFINITIONSThis source file includes following definitions.
1 /*- 2 * Copyright (c) 2005-2009, Kohsuke Ohtani 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the author nor the names of any co-contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * pm.c - power management driver 32 */ 33 34 #include <sys/ioctl.h> 35 #include <sys/capability.h> 36 #include <sys/power.h> 37 #include <sys/signal.h> 38 #include <driver.h> 39 #include <devctl.h> 40 #include <cons.h> 41 #include <pm.h> 42 43 /* #define DEBUG_PM 1 */ 44 45 #ifdef DEBUG_PM 46 #define DPRINTF(a) printf a 47 #else 48 #define DPRINTF(a) 49 #endif 50 51 struct pm_softc { 52 device_t dev; /* device object */ 53 int isopen; /* number of open counts */ 54 int policy; /* power management policy */ 55 int timer_active; /* true if pm timer is staring */ 56 timer_t timer; /* pm timer */ 57 u_long idlecnt; /* idle counter in sec */ 58 u_long dimtime; /* auto dim (lcd off) time in sec */ 59 u_long sustime; /* auto suspend time in sec */ 60 task_t powtask; /* task for power server */ 61 int lcd_on; /* true if lcd is off */ 62 device_t lcd_dev; /* lcd device */ 63 int lastevt; /* last event */ 64 }; 65 66 static int pm_open(device_t, int); 67 static int pm_close(device_t); 68 static int pm_ioctl(device_t, u_long, void *); 69 static int pm_init(struct driver *); 70 static void pm_stop_timer(void); 71 static void pm_update_timer(void); 72 static void pm_timeout(void *); 73 74 75 static struct devops pm_devops = { 76 /* open */ pm_open, 77 /* close */ pm_close, 78 /* read */ no_read, 79 /* write */ no_write, 80 /* ioctl */ pm_ioctl, 81 /* devctl */ no_devctl, 82 }; 83 84 struct driver pm_driver = { 85 /* name */ "pm", 86 /* devops */ &pm_devops, 87 /* devsz */ sizeof(struct pm_softc), 88 /* flags */ 0, 89 /* probe */ NULL, 90 /* init */ pm_init, 91 /* unload */ NULL, 92 }; 93 94 /* 95 * Pointer to the PM state. There can be only one PM instance. 96 */ 97 static struct pm_softc *pm_softc; 98 99 static int 100 pm_open(device_t dev, int mode) 101 { 102 struct pm_softc *sc = pm_softc; 103 104 if (!task_capable(CAP_POWERMGMT)) 105 return EPERM; 106 107 if (sc->isopen > 0) 108 return EBUSY; 109 110 sc->isopen++; 111 return 0; 112 } 113 114 static int 115 pm_close(device_t dev) 116 { 117 struct pm_softc *sc = pm_softc; 118 119 if (!task_capable(CAP_POWERMGMT)) 120 return EPERM; 121 122 if (sc->isopen != 1) 123 return EINVAL; 124 125 sc->isopen--; 126 return 0; 127 } 128 129 static int 130 pm_ioctl(device_t dev, u_long cmd, void *arg) 131 { 132 struct pm_softc *sc = pm_softc; 133 int error = 0; 134 int policy, state, event; 135 136 if (!task_capable(CAP_POWERMGMT)) 137 return EPERM; 138 139 switch (cmd) { 140 141 case PMIOC_CONNECT: 142 /* Connection request from the power server */ 143 if (copyin(arg, &sc->powtask, sizeof(task_t))) 144 return EFAULT; 145 DPRINTF(("pm: connect power server\n")); 146 break; 147 148 case PMIOC_QUERY_EVENT: 149 event = sc->lastevt; 150 sc->lastevt = PME_NO_EVENT; 151 if (copyout(&event, arg, sizeof(int))) 152 return EFAULT; 153 DPRINTF(("pm: query event=%d\n", event)); 154 break; 155 156 case PMIOC_SET_POWER: 157 if (copyin(arg, &state, sizeof(int))) 158 return EFAULT; 159 160 switch (state) { 161 case PWR_SUSPEND: 162 case PWR_OFF: 163 case PWR_REBOOT: 164 pm_set_power(state); 165 break; 166 default: 167 error = EINVAL; 168 break; 169 } 170 break; 171 172 case PMIOC_GET_POLICY: 173 if (copyout(&sc->policy, arg, sizeof(int))) 174 return EFAULT; 175 DPRINTF(("pm: get policy %d\n", sc->policy)); 176 break; 177 178 case PMIOC_SET_POLICY: 179 if (copyin(arg, &policy, sizeof(int))) 180 return EFAULT; 181 if (policy != PM_POWERSAVE && policy != PM_PERFORMANCE) 182 return EINVAL; 183 184 DPRINTF(("pm: set policy %d\n", policy)); 185 186 if (policy == sc->policy) { 187 /* same policy */ 188 break; 189 } 190 /* Call devctl() routine for all devices */ 191 device_broadcast(DEVCTL_PM_CHGPOLICY, &policy, 1); 192 193 sc->policy = policy; 194 if (policy == PM_POWERSAVE) 195 pm_update_timer(); 196 else 197 pm_stop_timer(); 198 break; 199 200 case PMIOC_GET_SUSTMR: 201 if (copyout(&sc->sustime, arg, sizeof(u_long))) 202 return EFAULT; 203 break; 204 205 case PMIOC_SET_SUSTMR: 206 if (copyin(arg, &sc->sustime, sizeof(u_long))) 207 return EFAULT; 208 DPRINTF(("pm: set sustmr=%d\n", sc->sustime)); 209 pm_update_timer(); 210 break; 211 212 case PMIOC_GET_DIMTMR: 213 if (copyout(&sc->dimtime, arg, sizeof(u_long))) 214 return EFAULT; 215 break; 216 217 case PMIOC_SET_DIMTMR: 218 if (copyin(arg, &sc->dimtime, sizeof(u_long))) 219 return EFAULT; 220 DPRINTF(("pm: set dimtmr=%d\n", sc->dimtime)); 221 pm_update_timer(); 222 break; 223 224 default: 225 return EINVAL; 226 } 227 return error; 228 } 229 230 231 static void 232 pm_stop_timer(void) 233 { 234 struct pm_softc *sc = pm_softc; 235 int s; 236 237 DPRINTF(("pm: stop timer\n")); 238 239 s = splhigh(); 240 if (sc->timer_active) { 241 timer_stop(&sc->timer); 242 sc->idlecnt = 0; 243 sc->timer_active = 0; 244 } 245 splx(s); 246 } 247 248 static void 249 pm_update_timer(void) 250 { 251 struct pm_softc *sc = pm_softc; 252 int s; 253 254 if (sc->policy != PM_POWERSAVE) 255 return; 256 257 s = splhigh(); 258 sc->idlecnt = 0; 259 if (sc->timer_active) { 260 if (sc->sustime == 0 && sc->dimtime == 0) 261 timer_stop(&sc->timer); 262 } else { 263 if (sc->sustime != 0 || sc->dimtime != 0) { 264 DPRINTF(("pm: start timer\n")); 265 timer_callout(&sc->timer, 1000, &pm_timeout, sc); 266 sc->timer_active = 1; 267 } 268 } 269 } 270 271 272 static int 273 pm_suspend(void) 274 { 275 int error; 276 277 DPRINTF(("pm: suspend system...\n")); 278 279 pm_stop_timer(); 280 error = device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1); 281 if (error) { 282 device_broadcast(DEVCTL_PM_POWERUP, NULL, 1); 283 return error; 284 } 285 machine_powerdown(PWR_SUSPEND); 286 return 0; 287 } 288 289 static int 290 pm_resume(void) 291 { 292 293 DPRINTF(("pm: resume...\n")); 294 295 device_broadcast(DEVCTL_PM_POWERUP, NULL, 1); 296 pm_update_timer(); 297 return 0; 298 } 299 300 static int 301 pm_poweroff(void) 302 { 303 304 DPRINTF(("pm: power off...\n")); 305 306 pm_stop_timer(); 307 device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1); 308 driver_shutdown(); 309 310 #ifdef CONFIG_CONS 311 cons_puts("\nThe system is halted. You can turn off power."); 312 #endif 313 machine_powerdown(PWR_OFF); 314 315 /* NOTREACHED */ 316 return 0; 317 } 318 319 static int 320 pm_reboot(void) 321 { 322 323 DPRINTF(("pm: rebooting...\n")); 324 325 pm_stop_timer(); 326 device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1); 327 driver_shutdown(); 328 machine_powerdown(PWR_REBOOT); 329 330 /* NOTREACHED */ 331 return 0; 332 } 333 334 static void 335 pm_lcd_off(void) 336 { 337 struct pm_softc *sc = pm_softc; 338 339 DPRINTF(("pm: LCD off\n")); 340 341 if (sc->lcd_dev != NODEV && sc->lcd_on) { 342 device_control(sc->lcd_dev, DEVCTL_PM_LCDOFF, NULL); 343 if (sc->sustime == 0) 344 pm_stop_timer(); 345 sc->lcd_on = 0; 346 } 347 } 348 349 static void 350 pm_lcd_on(void) 351 { 352 struct pm_softc *sc = pm_softc; 353 354 DPRINTF(("pm: LCD on\n")); 355 356 if (sc->lcd_dev != NODEV && !sc->lcd_on) { 357 device_control(sc->lcd_dev, DEVCTL_PM_LCDON, NULL); 358 pm_update_timer(); 359 sc->lcd_on = 1; 360 } 361 } 362 363 static void 364 pm_timeout(void *arg) 365 { 366 struct pm_softc *sc = arg; 367 int s, reload; 368 369 s = splhigh(); 370 sc->idlecnt++; 371 splx(s); 372 373 DPRINTF(("pm: idlecnt=%d\n", sc->idlecnt)); 374 375 if (sc->sustime != 0 && sc->idlecnt >= sc->sustime) { 376 #ifdef CONFIG_CONS 377 cons_puts("\nThe system is about to suspend..."); 378 #endif 379 pm_suspend(); 380 } else { 381 reload = 0; 382 if (sc->dimtime != 0 && sc->idlecnt >= sc->dimtime) { 383 pm_lcd_off(); 384 if (sc->sustime != 0) 385 reload = 1; 386 } else 387 reload = 1; 388 389 if (reload) 390 timer_callout(&sc->timer, 1000, &pm_timeout, sc); 391 } 392 } 393 394 /* 395 * PM service for other drivers. 396 */ 397 int 398 pm_set_power(int state) 399 { 400 int error; 401 402 switch (state) { 403 case PWR_ON: 404 error = pm_resume(); 405 break; 406 case PWR_SUSPEND: 407 error = pm_suspend(); 408 break; 409 case PWR_OFF: 410 error = pm_poweroff(); 411 break; 412 case PWR_REBOOT: 413 error = pm_reboot(); 414 break; 415 default: 416 error = EINVAL; 417 } 418 return error; 419 } 420 421 /* 422 * PM event notification. 423 */ 424 void 425 pm_notify(int event) 426 { 427 struct pm_softc *sc = pm_softc; 428 int s; 429 430 if (event == PME_USER_ACTIVITY) { 431 /* 432 * Reload suspend timer for user activity. 433 */ 434 s = splhigh(); 435 sc->idlecnt = 0; 436 splx(s); 437 438 if (!sc->lcd_on) 439 pm_lcd_on(); 440 return; 441 } 442 443 DPRINTF(("pm: notify %d\n", event)); 444 445 if (sc->powtask != TASK_NULL) { 446 /* 447 * Power server exists. 448 */ 449 switch (event) { 450 case PME_PWRBTN_PRESS: 451 case PME_SLPBTN_PRESS: 452 case PME_LOW_BATTERY: 453 case PME_LCD_CLOSE: 454 /* 455 * Post an exception to the power server. 456 * Then, the power server will query PM event. 457 */ 458 sc->lastevt = event; 459 DPRINTF(("pm: post %d\n", event)); 460 exception_post(sc->powtask, SIGPWR); 461 break; 462 case PME_LCD_OPEN: 463 sc->lastevt = PME_NO_EVENT; 464 pm_lcd_on(); 465 break; 466 } 467 } else { 468 /* 469 * No power server. 470 * Map power event to default action. 471 */ 472 switch (event) { 473 case PME_PWRBTN_PRESS: 474 pm_poweroff(); 475 break; 476 case PME_SLPBTN_PRESS: 477 case PME_LOW_BATTERY: 478 pm_suspend(); 479 break; 480 case PME_LCD_OPEN: 481 pm_lcd_on(); 482 break; 483 case PME_LCD_CLOSE: 484 pm_lcd_off(); 485 break; 486 } 487 } 488 } 489 490 void 491 pm_attach_lcd(device_t dev) 492 { 493 494 ASSERT(pm_softc != NULL); 495 496 pm_softc->lcd_dev = dev; 497 } 498 499 static int 500 pm_init(struct driver *self) 501 { 502 struct pm_softc *sc; 503 device_t dev; 504 505 /* Create device object */ 506 dev = device_create(self, "pm", D_CHR|D_PROT); 507 508 sc = device_private(dev); 509 sc->dev = dev; 510 sc->isopen = 0; 511 sc->policy = DEFAULT_POWER_POLICY; 512 sc->idlecnt = 0; 513 sc->dimtime = 0; 514 sc->sustime = 0; 515 sc->timer_active = 0; 516 sc->powtask = TASK_NULL; 517 sc->lcd_dev = NODEV; 518 sc->lcd_on = 1; 519 sc->lastevt = PME_NO_EVENT; 520 521 pm_softc = sc; 522 523 DPRINTF(("Power policy: %s mode\n", 524 (sc->policy == PM_POWERSAVE) ? "power save" : "performance")); 525 return 0; 526 } /* [<][>][^][v][top][bottom][index][help] */ | |||
Copyright© 2005-2009 Kohsuke Ohtani |