1 /* $NetBSD: sysmon_power.c,v 1.3 2003/04/20 20:48:28 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Power management framework for sysmon. 40 * 41 * We defer to a power management daemon running in userspace, since 42 * power management is largely a policy issue. This merely provides 43 * for power management event notification to that daemon. 44 */ 45 46 #include <sys/param.h> 47 #include <sys/reboot.h> 48 #include <sys/systm.h> 49 #include <sys/poll.h> 50 #include <sys/select.h> 51 #include <sys/vnode.h> 52 53 #include <dev/sysmon/sysmonvar.h> 54 55 static LIST_HEAD(, sysmon_pswitch) sysmon_pswitch_list = 56 LIST_HEAD_INITIALIZER(sysmon_pswitch_list); 57 static struct simplelock sysmon_pswitch_list_slock = 58 SIMPLELOCK_INITIALIZER; 59 60 static struct proc *sysmon_power_daemon; 61 62 #define SYSMON_MAX_POWER_EVENTS 32 63 64 static struct simplelock sysmon_power_event_queue_slock = 65 SIMPLELOCK_INITIALIZER; 66 static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS]; 67 static int sysmon_power_event_queue_head; 68 static int sysmon_power_event_queue_tail; 69 static int sysmon_power_event_queue_count; 70 static int sysmon_power_event_queue_flags; 71 static struct selinfo sysmon_power_event_queue_selinfo; 72 73 static char sysmon_power_type[32]; 74 75 #define PEVQ_F_WAITING 0x01 /* daemon waiting for event */ 76 77 #define SYSMON_NEXT_EVENT(x) (((x) + 1) / SYSMON_MAX_POWER_EVENTS) 78 79 /* 80 * sysmon_queue_power_event: 81 * 82 * Enqueue a power event for the power mangement daemon. Returns 83 * non-zero if we were able to enqueue a power event. 84 */ 85 static int 86 sysmon_queue_power_event(power_event_t *pev) 87 { 88 89 LOCK_ASSERT(simple_lock_held(&sysmon_power_event_queue_slock)); 90 91 if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS) 92 return (0); 93 94 sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev; 95 sysmon_power_event_queue_head = 96 SYSMON_NEXT_EVENT(sysmon_power_event_queue_head); 97 sysmon_power_event_queue_count++; 98 99 if (sysmon_power_event_queue_flags & PEVQ_F_WAITING) { 100 sysmon_power_event_queue_flags &= ~PEVQ_F_WAITING; 101 wakeup(&sysmon_power_event_queue_count); 102 } 103 selnotify(&sysmon_power_event_queue_selinfo, 0); 104 105 return (1); 106 } 107 108 /* 109 * sysmon_get_power_event: 110 * 111 * Get a power event from the queue. Returns non-zero if there 112 * is an event available. 113 */ 114 static int 115 sysmon_get_power_event(power_event_t *pev) 116 { 117 118 LOCK_ASSERT(simple_lock_held(&sysmon_power_event_queue_slock)); 119 120 if (sysmon_power_event_queue_count == 0) 121 return (0); 122 123 *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail]; 124 sysmon_power_event_queue_tail = 125 SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail); 126 sysmon_power_event_queue_count--; 127 128 return (1); 129 } 130 131 /* 132 * sysmon_power_event_queue_flush: 133 * 134 * Flush the event queue, and reset all state. 135 */ 136 static void 137 sysmon_power_event_queue_flush(void) 138 { 139 140 sysmon_power_event_queue_head = 0; 141 sysmon_power_event_queue_tail = 0; 142 sysmon_power_event_queue_count = 0; 143 sysmon_power_event_queue_flags = 0; 144 } 145 146 /* 147 * sysmonopen_power: 148 * 149 * Open the system monitor device. 150 */ 151 int 152 sysmonopen_power(dev_t dev, int flag, int mode, struct proc *p) 153 { 154 int error = 0; 155 156 simple_lock(&sysmon_power_event_queue_slock); 157 if (sysmon_power_daemon != NULL) 158 error = EBUSY; 159 else { 160 sysmon_power_daemon = p; 161 sysmon_power_event_queue_flush(); 162 } 163 simple_unlock(&sysmon_power_event_queue_slock); 164 165 return (error); 166 } 167 168 /* 169 * sysmonclose_power: 170 * 171 * Close the system monitor device. 172 */ 173 int 174 sysmonclose_power(dev_t dev, int flag, int mode, struct proc *p) 175 { 176 int count; 177 178 simple_lock(&sysmon_power_event_queue_slock); 179 count = sysmon_power_event_queue_count; 180 sysmon_power_daemon = NULL; 181 sysmon_power_event_queue_flush(); 182 simple_unlock(&sysmon_power_event_queue_slock); 183 184 if (count) 185 printf("WARNING: %d power events lost by exiting daemon\n", 186 count); 187 188 return (0); 189 } 190 191 /* 192 * sysmonread_power: 193 * 194 * Read the system monitor device. 195 */ 196 int 197 sysmonread_power(dev_t dev, struct uio *uio, int flags) 198 { 199 power_event_t pev; 200 int error; 201 202 /* We only allow one event to be read at a time. */ 203 if (uio->uio_resid != POWER_EVENT_MSG_SIZE) 204 return (EINVAL); 205 206 simple_lock(&sysmon_power_event_queue_slock); 207 again: 208 if (sysmon_get_power_event(&pev)) { 209 simple_unlock(&sysmon_power_event_queue_slock); 210 return (uiomove(&pev, POWER_EVENT_MSG_SIZE, uio)); 211 } 212 213 if (flags & IO_NDELAY) { 214 simple_unlock(&sysmon_power_event_queue_slock); 215 return (EWOULDBLOCK); 216 } 217 218 sysmon_power_event_queue_flags |= PEVQ_F_WAITING; 219 error = ltsleep(&sysmon_power_event_queue_count, 220 PRIBIO|PCATCH, "smpower", 0, &sysmon_power_event_queue_slock); 221 if (error) { 222 simple_unlock(&sysmon_power_event_queue_slock); 223 return (error); 224 } 225 goto again; 226 } 227 228 /* 229 * sysmonpoll_power: 230 * 231 * Poll the system monitor device. 232 */ 233 int 234 sysmonpoll_power(dev_t dev, int events, struct proc *p) 235 { 236 int revents; 237 238 revents = events & (POLLOUT | POLLWRNORM); 239 240 /* Attempt to save some work. */ 241 if ((events & (POLLIN | POLLRDNORM)) == 0) 242 return (revents); 243 244 simple_lock(&sysmon_power_event_queue_slock); 245 if (sysmon_power_event_queue_count) 246 revents |= events & (POLLIN | POLLRDNORM); 247 else 248 selrecord(p, &sysmon_power_event_queue_selinfo); 249 simple_unlock(&sysmon_power_event_queue_slock); 250 251 return (revents); 252 } 253 254 static void 255 filt_sysmon_power_rdetach(struct knote *kn) 256 { 257 258 simple_lock(&sysmon_power_event_queue_slock); 259 SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist, 260 kn, knote, kn_selnext); 261 simple_unlock(&sysmon_power_event_queue_slock); 262 } 263 264 static int 265 filt_sysmon_power_read(struct knote *kn, long hint) 266 { 267 268 simple_lock(&sysmon_power_event_queue_slock); 269 kn->kn_data = sysmon_power_event_queue_count; 270 simple_unlock(&sysmon_power_event_queue_slock); 271 272 return (kn->kn_data > 0); 273 } 274 275 static const struct filterops sysmon_power_read_filtops = 276 { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read }; 277 278 static const struct filterops sysmon_power_write_filtops = 279 { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue }; 280 281 /* 282 * sysmonkqfilter_power: 283 * 284 * Kqueue filter for the system monitor device. 285 */ 286 int 287 sysmonkqfilter_power(dev_t dev, struct knote *kn) 288 { 289 struct klist *klist; 290 291 switch (kn->kn_filter) { 292 case EVFILT_READ: 293 klist = &sysmon_power_event_queue_selinfo.sel_klist; 294 kn->kn_fop = &sysmon_power_read_filtops; 295 break; 296 297 case EVFILT_WRITE: 298 klist = &sysmon_power_event_queue_selinfo.sel_klist; 299 kn->kn_fop = &sysmon_power_write_filtops; 300 break; 301 302 default: 303 return (1); 304 } 305 306 simple_lock(&sysmon_power_event_queue_slock); 307 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 308 simple_unlock(&sysmon_power_event_queue_slock); 309 310 return (0); 311 } 312 313 /* 314 * sysmonioctl_power: 315 * 316 * Perform a power managmenet control request. 317 */ 318 int 319 sysmonioctl_power(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 320 { 321 int error = 0; 322 323 switch (cmd) { 324 case POWER_IOC_GET_TYPE: 325 { 326 struct power_type *power_type = (void *) data; 327 328 strcpy(power_type->power_type, sysmon_power_type); 329 break; 330 } 331 default: 332 error = ENOTTY; 333 } 334 335 return (error); 336 } 337 338 /* 339 * sysmon_power_settype: 340 * 341 * Sets the back-end power management type. This information can 342 * be used by the power management daemon. 343 */ 344 void 345 sysmon_power_settype(const char *type) 346 { 347 348 /* 349 * Don't bother locking this; it's going to be set 350 * during autoconfiguration, and then only read from 351 * then on. 352 */ 353 strcpy(sysmon_power_type, type); 354 } 355 356 /* 357 * sysmon_pswitch_register: 358 * 359 * Register a power switch device. 360 */ 361 int 362 sysmon_pswitch_register(struct sysmon_pswitch *smpsw) 363 { 364 365 simple_lock(&sysmon_pswitch_list_slock); 366 LIST_INSERT_HEAD(&sysmon_pswitch_list, smpsw, smpsw_list); 367 simple_unlock(&sysmon_pswitch_list_slock); 368 369 return (0); 370 } 371 372 /* 373 * sysmon_pswitch_unregister: 374 * 375 * Unregister a power switch device. 376 */ 377 void 378 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) 379 { 380 381 simple_lock(&sysmon_pswitch_list_slock); 382 LIST_REMOVE(smpsw, smpsw_list); 383 simple_unlock(&sysmon_pswitch_list_slock); 384 } 385 386 /* 387 * sysmon_pswitch_event: 388 * 389 * Register an event on a power switch device. 390 */ 391 void 392 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) 393 { 394 395 /* 396 * If a power management daemon is connected, then simply 397 * deliver the event to them. If not, we need to try to 398 * do something reasonable ourselves. 399 */ 400 simple_lock(&sysmon_power_event_queue_slock); 401 if (sysmon_power_daemon != NULL) { 402 power_event_t pev; 403 int rv; 404 405 pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE; 406 pev.pev_switch.psws_state = event; 407 pev.pev_switch.psws_type = smpsw->smpsw_type; 408 strcpy(pev.pev_switch.psws_name, smpsw->smpsw_name); 409 410 rv = sysmon_queue_power_event(&pev); 411 simple_unlock(&sysmon_power_event_queue_slock); 412 if (rv == 0) 413 printf("%s: WARNING: state change event lost; " 414 "queue full\n", smpsw->smpsw_name, 415 pev.pev_type); 416 return; 417 } 418 simple_unlock(&sysmon_power_event_queue_slock); 419 420 switch (smpsw->smpsw_type) { 421 case PSWITCH_TYPE_POWER: 422 if (event != PSWITCH_EVENT_PRESSED) { 423 /* just ignore it */ 424 return; 425 } 426 427 /* 428 * Attempt a somewhat graceful shutdown of the system, 429 * as if the user has issued a reboot(2) call with 430 * RB_POWERDOWN. 431 */ 432 printf("%s: power button pressed, shutting down!\n", 433 smpsw->smpsw_name); 434 cpu_reboot(RB_POWERDOWN, NULL); 435 break; 436 437 case PSWITCH_TYPE_RESET: 438 if (event != PSWITCH_EVENT_PRESSED) { 439 /* just ignore it */ 440 return; 441 } 442 443 /* 444 * Attempt a somewhat graceful reboot of the system, 445 * as if the user had issued a reboot(2) call. 446 */ 447 printf("%s: reset button pressed, rebooting!\n", 448 smpsw->smpsw_name); 449 cpu_reboot(0, NULL); 450 break; 451 452 case PSWITCH_TYPE_SLEEP: 453 if (event != PSWITCH_EVENT_PRESSED) { 454 /* just ignore it */ 455 return; 456 } 457 458 /* 459 * Try to enter a "sleep" state. 460 */ 461 /* XXX */ 462 printf("%s: sleep button pressed.\n", smpsw->smpsw_name); 463 break; 464 465 case PSWITCH_TYPE_LID: 466 switch (event) { 467 case PSWITCH_EVENT_PRESSED: 468 /* 469 * Try to enter a "standby" state. 470 */ 471 /* XXX */ 472 printf("%s: lid closed.\n", smpsw->smpsw_name); 473 break; 474 475 case PSWITCH_EVENT_RELEASED: 476 /* 477 * Come out of "standby" state. 478 */ 479 /* XXX */ 480 printf("%s: lid opened.\n", smpsw->smpsw_name); 481 break; 482 483 default: 484 printf("%s: unknown lid switch event: %d\n", 485 smpsw->smpsw_name, event); 486 } 487 break; 488 489 default: 490 printf("%s: sysmon_pswitch_event can't handle me.\n", 491 smpsw->smpsw_name); 492 } 493 } 494