| 1 | /* $NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 2007 Juan Romero Pardines. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * 1. Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in the |
| 14 | * documentation and/or other materials provided with the distribution. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | /* |
| 29 | * Copyright (c) 2003 Wasabi Systems, Inc. |
| 30 | * All rights reserved. |
| 31 | * |
| 32 | * Written by Jason R. Thorpe for Wasabi Systems, Inc. |
| 33 | * |
| 34 | * Redistribution and use in source and binary forms, with or without |
| 35 | * modification, are permitted provided that the following conditions |
| 36 | * are met: |
| 37 | * 1. Redistributions of source code must retain the above copyright |
| 38 | * notice, this list of conditions and the following disclaimer. |
| 39 | * 2. Redistributions in binary form must reproduce the above copyright |
| 40 | * notice, this list of conditions and the following disclaimer in the |
| 41 | * documentation and/or other materials provided with the distribution. |
| 42 | * 3. All advertising materials mentioning features or use of this software |
| 43 | * must display the following acknowledgement: |
| 44 | * This product includes software developed for the NetBSD Project by |
| 45 | * Wasabi Systems, Inc. |
| 46 | * 4. The name of Wasabi Systems, Inc. may not be used to endorse |
| 47 | * or promote products derived from this software without specific prior |
| 48 | * written permission. |
| 49 | * |
| 50 | * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND |
| 51 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 52 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 53 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC |
| 54 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 55 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 56 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 57 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 58 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 59 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 60 | * POSSIBILITY OF SUCH DAMAGE. |
| 61 | */ |
| 62 | |
| 63 | /* |
| 64 | * Power management framework for sysmon. |
| 65 | * |
| 66 | * We defer to a power management daemon running in userspace, since |
| 67 | * power management is largely a policy issue. This merely provides |
| 68 | * for power management event notification to that daemon. |
| 69 | */ |
| 70 | |
| 71 | #include <sys/cdefs.h> |
| 72 | __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $" ); |
| 73 | |
| 74 | #ifdef _KERNEL_OPT |
| 75 | #include "opt_compat_netbsd.h" |
| 76 | #endif |
| 77 | |
| 78 | #include <sys/param.h> |
| 79 | #include <sys/reboot.h> |
| 80 | #include <sys/systm.h> |
| 81 | #include <sys/poll.h> |
| 82 | #include <sys/select.h> |
| 83 | #include <sys/vnode.h> |
| 84 | #include <sys/condvar.h> |
| 85 | #include <sys/mutex.h> |
| 86 | #include <sys/kmem.h> |
| 87 | #include <sys/proc.h> |
| 88 | #include <sys/device.h> |
| 89 | #include <sys/rndsource.h> |
| 90 | #include <sys/module.h> |
| 91 | #include <sys/once.h> |
| 92 | |
| 93 | #include <dev/sysmon/sysmonvar.h> |
| 94 | #include <prop/proplib.h> |
| 95 | |
| 96 | MODULE(MODULE_CLASS_DRIVER, sysmon_power, "sysmon" ); |
| 97 | |
| 98 | /* |
| 99 | * Singly linked list for dictionaries to be stored/sent. |
| 100 | */ |
| 101 | struct power_event_dictionary { |
| 102 | SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head; |
| 103 | prop_dictionary_t dict; |
| 104 | int flags; |
| 105 | }; |
| 106 | |
| 107 | struct power_event_description { |
| 108 | int type; |
| 109 | const char *desc; |
| 110 | }; |
| 111 | |
| 112 | /* |
| 113 | * Available events for power switches. |
| 114 | */ |
| 115 | static const struct power_event_description pswitch_event_desc[] = { |
| 116 | { PSWITCH_EVENT_PRESSED, "pressed" }, |
| 117 | { PSWITCH_EVENT_RELEASED, "released" }, |
| 118 | { -1, NULL } |
| 119 | }; |
| 120 | |
| 121 | /* |
| 122 | * Available script names for power switches. |
| 123 | */ |
| 124 | static const struct power_event_description pswitch_type_desc[] = { |
| 125 | { PSWITCH_TYPE_POWER, "power_button" }, |
| 126 | { PSWITCH_TYPE_SLEEP, "sleep_button" }, |
| 127 | { PSWITCH_TYPE_LID, "lid_switch" }, |
| 128 | { PSWITCH_TYPE_RESET, "reset_button" }, |
| 129 | { PSWITCH_TYPE_ACADAPTER, "acadapter" }, |
| 130 | { PSWITCH_TYPE_HOTKEY, "hotkey_button" }, |
| 131 | { PSWITCH_TYPE_RADIO, "radio_button" }, |
| 132 | { -1, NULL } |
| 133 | }; |
| 134 | |
| 135 | /* |
| 136 | * Available events for envsys(4). |
| 137 | */ |
| 138 | static const struct power_event_description penvsys_event_desc[] = { |
| 139 | { PENVSYS_EVENT_NORMAL, "normal" }, |
| 140 | { PENVSYS_EVENT_CRITICAL, "critical" }, |
| 141 | { PENVSYS_EVENT_CRITOVER, "critical-over" }, |
| 142 | { PENVSYS_EVENT_CRITUNDER, "critical-under" }, |
| 143 | { PENVSYS_EVENT_WARNOVER, "warning-over" }, |
| 144 | { PENVSYS_EVENT_WARNUNDER, "warning-under" }, |
| 145 | { PENVSYS_EVENT_BATT_CRIT, "critical-capacity" }, |
| 146 | { PENVSYS_EVENT_BATT_WARN, "warning-capacity" }, |
| 147 | { PENVSYS_EVENT_BATT_HIGH, "high-capacity" }, |
| 148 | { PENVSYS_EVENT_BATT_MAX, "maximum-capacity" }, |
| 149 | { PENVSYS_EVENT_STATE_CHANGED, "state-changed" }, |
| 150 | { PENVSYS_EVENT_LOW_POWER, "low-power" }, |
| 151 | { -1, NULL } |
| 152 | }; |
| 153 | |
| 154 | /* |
| 155 | * Available script names for envsys(4). |
| 156 | */ |
| 157 | static const struct power_event_description penvsys_type_desc[] = { |
| 158 | { PENVSYS_TYPE_BATTERY, "sensor_battery" }, |
| 159 | { PENVSYS_TYPE_DRIVE, "sensor_drive" }, |
| 160 | { PENVSYS_TYPE_FAN, "sensor_fan" }, |
| 161 | { PENVSYS_TYPE_INDICATOR, "sensor_indicator" }, |
| 162 | { PENVSYS_TYPE_POWER, "sensor_power" }, |
| 163 | { PENVSYS_TYPE_RESISTANCE, "sensor_resistance" }, |
| 164 | { PENVSYS_TYPE_TEMP, "sensor_temperature" }, |
| 165 | { PENVSYS_TYPE_VOLTAGE, "sensor_voltage" }, |
| 166 | { -1, NULL } |
| 167 | }; |
| 168 | |
| 169 | #define SYSMON_MAX_POWER_EVENTS 32 |
| 170 | #define SYSMON_POWER_DICTIONARY_BUSY 0x01 |
| 171 | #define SYSMON_POWER_DICTIONARY_READY 0x02 |
| 172 | |
| 173 | static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS]; |
| 174 | static int sysmon_power_event_queue_head; |
| 175 | static int sysmon_power_event_queue_tail; |
| 176 | static int sysmon_power_event_queue_count; |
| 177 | |
| 178 | static krndsource_t sysmon_rndsource; |
| 179 | |
| 180 | static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list = |
| 181 | SIMPLEQ_HEAD_INITIALIZER(pev_dict_list); |
| 182 | |
| 183 | static struct selinfo sysmon_power_event_queue_selinfo; |
| 184 | static struct lwp *sysmon_power_daemon; |
| 185 | |
| 186 | static kmutex_t sysmon_power_event_queue_mtx; |
| 187 | static kcondvar_t sysmon_power_event_queue_cv; |
| 188 | |
| 189 | static char sysmon_power_type[32]; |
| 190 | |
| 191 | static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int); |
| 192 | static int sysmon_power_daemon_task(struct power_event_dictionary *, |
| 193 | void *, int); |
| 194 | static void sysmon_power_destroy_dictionary(struct power_event_dictionary *); |
| 195 | |
| 196 | static struct sysmon_opvec sysmon_power_opvec = { |
| 197 | sysmonopen_power, sysmonclose_power, sysmonioctl_power, |
| 198 | sysmonread_power, sysmonpoll_power, sysmonkqfilter_power |
| 199 | }; |
| 200 | |
| 201 | #define SYSMON_NEXT_EVENT(x) (((x) + 1) % SYSMON_MAX_POWER_EVENTS) |
| 202 | |
| 203 | ONCE_DECL(once_power); |
| 204 | |
| 205 | static int |
| 206 | power_preinit(void) |
| 207 | { |
| 208 | |
| 209 | mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE); |
| 210 | cv_init(&sysmon_power_event_queue_cv, "smpower" ); |
| 211 | |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | /* |
| 216 | * sysmon_power_init: |
| 217 | * |
| 218 | * Initializes the mutexes and condition variables in the |
| 219 | * boot process via module initialization process. |
| 220 | */ |
| 221 | int |
| 222 | sysmon_power_init(void) |
| 223 | { |
| 224 | int error; |
| 225 | |
| 226 | (void)RUN_ONCE(&once_power, power_preinit); |
| 227 | |
| 228 | selinit(&sysmon_power_event_queue_selinfo); |
| 229 | |
| 230 | rnd_attach_source(&sysmon_rndsource, "system-power" , |
| 231 | RND_TYPE_POWER, RND_FLAG_DEFAULT); |
| 232 | |
| 233 | error = sysmon_attach_minor(SYSMON_MINOR_POWER, &sysmon_power_opvec); |
| 234 | |
| 235 | return error; |
| 236 | } |
| 237 | |
| 238 | int |
| 239 | sysmon_power_fini(void) |
| 240 | { |
| 241 | int error; |
| 242 | |
| 243 | if (sysmon_power_daemon != NULL) |
| 244 | error = EBUSY; |
| 245 | else |
| 246 | error = sysmon_attach_minor(SYSMON_MINOR_POWER, NULL); |
| 247 | |
| 248 | if (error == 0) { |
| 249 | rnd_detach_source(&sysmon_rndsource); |
| 250 | seldestroy(&sysmon_power_event_queue_selinfo); |
| 251 | cv_destroy(&sysmon_power_event_queue_cv); |
| 252 | mutex_destroy(&sysmon_power_event_queue_mtx); |
| 253 | } |
| 254 | |
| 255 | return error; |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 | * sysmon_queue_power_event: |
| 260 | * |
| 261 | * Enqueue a power event for the power management daemon. Returns |
| 262 | * non-zero if we were able to enqueue a power event. |
| 263 | */ |
| 264 | static int |
| 265 | sysmon_queue_power_event(power_event_t *pev) |
| 266 | { |
| 267 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
| 268 | |
| 269 | if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS) |
| 270 | return 0; |
| 271 | |
| 272 | sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev; |
| 273 | sysmon_power_event_queue_head = |
| 274 | SYSMON_NEXT_EVENT(sysmon_power_event_queue_head); |
| 275 | sysmon_power_event_queue_count++; |
| 276 | |
| 277 | return 1; |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 | * sysmon_get_power_event: |
| 282 | * |
| 283 | * Get a power event from the queue. Returns non-zero if there |
| 284 | * is an event available. |
| 285 | */ |
| 286 | static int |
| 287 | sysmon_get_power_event(power_event_t *pev) |
| 288 | { |
| 289 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
| 290 | |
| 291 | if (sysmon_power_event_queue_count == 0) |
| 292 | return 0; |
| 293 | |
| 294 | *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail]; |
| 295 | sysmon_power_event_queue_tail = |
| 296 | SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail); |
| 297 | sysmon_power_event_queue_count--; |
| 298 | |
| 299 | return 1; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | * sysmon_power_event_queue_flush: |
| 304 | * |
| 305 | * Flush the event queue, and reset all state. |
| 306 | */ |
| 307 | static void |
| 308 | sysmon_power_event_queue_flush(void) |
| 309 | { |
| 310 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
| 311 | |
| 312 | sysmon_power_event_queue_head = 0; |
| 313 | sysmon_power_event_queue_tail = 0; |
| 314 | sysmon_power_event_queue_count = 0; |
| 315 | } |
| 316 | |
| 317 | /* |
| 318 | * sysmon_power_daemon_task: |
| 319 | * |
| 320 | * Assign required power event members and sends a signal |
| 321 | * to the process to notify that an event was enqueued successfully. |
| 322 | */ |
| 323 | static int |
| 324 | sysmon_power_daemon_task(struct power_event_dictionary *ped, |
| 325 | void *pev_data, int event) |
| 326 | { |
| 327 | power_event_t pev; |
| 328 | int rv, error = 0; |
| 329 | |
| 330 | if (!ped || !ped->dict || !pev_data) |
| 331 | return EINVAL; |
| 332 | |
| 333 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 334 | |
| 335 | switch (event) { |
| 336 | /* |
| 337 | * Power switch events. |
| 338 | */ |
| 339 | case PSWITCH_EVENT_PRESSED: |
| 340 | case PSWITCH_EVENT_RELEASED: |
| 341 | { |
| 342 | |
| 343 | struct sysmon_pswitch *pswitch = |
| 344 | (struct sysmon_pswitch *)pev_data; |
| 345 | |
| 346 | pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE; |
| 347 | #ifdef COMPAT_40 |
| 348 | pev.pev_switch.psws_state = event; |
| 349 | pev.pev_switch.psws_type = pswitch->smpsw_type; |
| 350 | |
| 351 | if (pswitch->smpsw_name) { |
| 352 | (void)strlcpy(pev.pev_switch.psws_name, |
| 353 | pswitch->smpsw_name, |
| 354 | sizeof(pev.pev_switch.psws_name)); |
| 355 | } |
| 356 | #endif |
| 357 | error = sysmon_power_make_dictionary(ped->dict, |
| 358 | pswitch, |
| 359 | event, |
| 360 | pev.pev_type); |
| 361 | if (error) { |
| 362 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 363 | goto out; |
| 364 | } |
| 365 | |
| 366 | break; |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | * ENVSYS events. |
| 371 | */ |
| 372 | case PENVSYS_EVENT_NORMAL: |
| 373 | case PENVSYS_EVENT_CRITICAL: |
| 374 | case PENVSYS_EVENT_CRITUNDER: |
| 375 | case PENVSYS_EVENT_CRITOVER: |
| 376 | case PENVSYS_EVENT_WARNUNDER: |
| 377 | case PENVSYS_EVENT_WARNOVER: |
| 378 | case PENVSYS_EVENT_BATT_CRIT: |
| 379 | case PENVSYS_EVENT_BATT_WARN: |
| 380 | case PENVSYS_EVENT_BATT_HIGH: |
| 381 | case PENVSYS_EVENT_BATT_MAX: |
| 382 | case PENVSYS_EVENT_STATE_CHANGED: |
| 383 | case PENVSYS_EVENT_LOW_POWER: |
| 384 | { |
| 385 | struct penvsys_state *penvsys = |
| 386 | (struct penvsys_state *)pev_data; |
| 387 | |
| 388 | pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE; |
| 389 | |
| 390 | error = sysmon_power_make_dictionary(ped->dict, |
| 391 | penvsys, |
| 392 | event, |
| 393 | pev.pev_type); |
| 394 | if (error) { |
| 395 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 396 | goto out; |
| 397 | } |
| 398 | |
| 399 | break; |
| 400 | } |
| 401 | default: |
| 402 | error = ENOTTY; |
| 403 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 404 | goto out; |
| 405 | } |
| 406 | |
| 407 | /* |
| 408 | * Enqueue the event. |
| 409 | */ |
| 410 | rv = sysmon_queue_power_event(&pev); |
| 411 | if (rv == 0) { |
| 412 | printf("%s: WARNING: state change event %d lost; " |
| 413 | "queue full\n" , __func__, pev.pev_type); |
| 414 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 415 | error = EINVAL; |
| 416 | goto out; |
| 417 | } else { |
| 418 | /* |
| 419 | * Notify the daemon that an event is ready and its |
| 420 | * dictionary is ready to be fetched. |
| 421 | */ |
| 422 | ped->flags |= SYSMON_POWER_DICTIONARY_READY; |
| 423 | SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head); |
| 424 | cv_broadcast(&sysmon_power_event_queue_cv); |
| 425 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 426 | selnotify(&sysmon_power_event_queue_selinfo, 0, 0); |
| 427 | } |
| 428 | |
| 429 | out: |
| 430 | return error; |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * sysmonopen_power: |
| 435 | * |
| 436 | * Open the system monitor device. |
| 437 | */ |
| 438 | int |
| 439 | sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l) |
| 440 | { |
| 441 | int error = 0; |
| 442 | |
| 443 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 444 | if (sysmon_power_daemon != NULL) |
| 445 | error = EBUSY; |
| 446 | else { |
| 447 | sysmon_power_daemon = l; |
| 448 | sysmon_power_event_queue_flush(); |
| 449 | } |
| 450 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 451 | |
| 452 | return error; |
| 453 | } |
| 454 | |
| 455 | /* |
| 456 | * sysmonclose_power: |
| 457 | * |
| 458 | * Close the system monitor device. |
| 459 | */ |
| 460 | int |
| 461 | sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l) |
| 462 | { |
| 463 | int count; |
| 464 | |
| 465 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 466 | count = sysmon_power_event_queue_count; |
| 467 | sysmon_power_daemon = NULL; |
| 468 | sysmon_power_event_queue_flush(); |
| 469 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 470 | |
| 471 | if (count) |
| 472 | printf("WARNING: %d power event%s lost by exiting daemon\n" , |
| 473 | count, count > 1 ? "s" : "" ); |
| 474 | |
| 475 | return 0; |
| 476 | } |
| 477 | |
| 478 | /* |
| 479 | * sysmonread_power: |
| 480 | * |
| 481 | * Read the system monitor device. |
| 482 | */ |
| 483 | int |
| 484 | sysmonread_power(dev_t dev, struct uio *uio, int flags) |
| 485 | { |
| 486 | power_event_t pev; |
| 487 | int rv; |
| 488 | |
| 489 | /* We only allow one event to be read at a time. */ |
| 490 | if (uio->uio_resid != POWER_EVENT_MSG_SIZE) |
| 491 | return EINVAL; |
| 492 | |
| 493 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 494 | for (;;) { |
| 495 | if (sysmon_get_power_event(&pev)) { |
| 496 | rv = uiomove(&pev, POWER_EVENT_MSG_SIZE, uio); |
| 497 | break; |
| 498 | } |
| 499 | |
| 500 | if (flags & IO_NDELAY) { |
| 501 | rv = EWOULDBLOCK; |
| 502 | break; |
| 503 | } |
| 504 | |
| 505 | cv_wait(&sysmon_power_event_queue_cv, |
| 506 | &sysmon_power_event_queue_mtx); |
| 507 | } |
| 508 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 509 | |
| 510 | return rv; |
| 511 | } |
| 512 | |
| 513 | /* |
| 514 | * sysmonpoll_power: |
| 515 | * |
| 516 | * Poll the system monitor device. |
| 517 | */ |
| 518 | int |
| 519 | sysmonpoll_power(dev_t dev, int events, struct lwp *l) |
| 520 | { |
| 521 | int revents; |
| 522 | |
| 523 | revents = events & (POLLOUT | POLLWRNORM); |
| 524 | |
| 525 | /* Attempt to save some work. */ |
| 526 | if ((events & (POLLIN | POLLRDNORM)) == 0) |
| 527 | return revents; |
| 528 | |
| 529 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 530 | if (sysmon_power_event_queue_count) |
| 531 | revents |= events & (POLLIN | POLLRDNORM); |
| 532 | else |
| 533 | selrecord(l, &sysmon_power_event_queue_selinfo); |
| 534 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 535 | |
| 536 | return revents; |
| 537 | } |
| 538 | |
| 539 | static void |
| 540 | filt_sysmon_power_rdetach(struct knote *kn) |
| 541 | { |
| 542 | |
| 543 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 544 | SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist, |
| 545 | kn, knote, kn_selnext); |
| 546 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 547 | } |
| 548 | |
| 549 | static int |
| 550 | filt_sysmon_power_read(struct knote *kn, long hint) |
| 551 | { |
| 552 | |
| 553 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 554 | kn->kn_data = sysmon_power_event_queue_count; |
| 555 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 556 | |
| 557 | return kn->kn_data > 0; |
| 558 | } |
| 559 | |
| 560 | static const struct filterops sysmon_power_read_filtops = |
| 561 | { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read }; |
| 562 | |
| 563 | static const struct filterops sysmon_power_write_filtops = |
| 564 | { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue }; |
| 565 | |
| 566 | /* |
| 567 | * sysmonkqfilter_power: |
| 568 | * |
| 569 | * Kqueue filter for the system monitor device. |
| 570 | */ |
| 571 | int |
| 572 | sysmonkqfilter_power(dev_t dev, struct knote *kn) |
| 573 | { |
| 574 | struct klist *klist; |
| 575 | |
| 576 | switch (kn->kn_filter) { |
| 577 | case EVFILT_READ: |
| 578 | klist = &sysmon_power_event_queue_selinfo.sel_klist; |
| 579 | kn->kn_fop = &sysmon_power_read_filtops; |
| 580 | break; |
| 581 | |
| 582 | case EVFILT_WRITE: |
| 583 | klist = &sysmon_power_event_queue_selinfo.sel_klist; |
| 584 | kn->kn_fop = &sysmon_power_write_filtops; |
| 585 | break; |
| 586 | |
| 587 | default: |
| 588 | return EINVAL; |
| 589 | } |
| 590 | |
| 591 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 592 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
| 593 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 594 | |
| 595 | return 0; |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | * sysmonioctl_power: |
| 600 | * |
| 601 | * Perform a power management control request. |
| 602 | */ |
| 603 | int |
| 604 | sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
| 605 | { |
| 606 | int error = 0; |
| 607 | |
| 608 | switch (cmd) { |
| 609 | case POWER_IOC_GET_TYPE: |
| 610 | case POWER_IOC_GET_TYPE_WITH_LOSSAGE: |
| 611 | { |
| 612 | struct power_type *power_type = (void *) data; |
| 613 | |
| 614 | (void)strlcpy(power_type->power_type, |
| 615 | sysmon_power_type, |
| 616 | sizeof(power_type->power_type)); |
| 617 | break; |
| 618 | } |
| 619 | case POWER_EVENT_RECVDICT: |
| 620 | { |
| 621 | struct plistref *plist = (struct plistref *)data; |
| 622 | struct power_event_dictionary *ped; |
| 623 | |
| 624 | /* |
| 625 | * Get the first dictionary enqueued and mark it |
| 626 | * as busy. |
| 627 | */ |
| 628 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 629 | ped = SIMPLEQ_FIRST(&pev_dict_list); |
| 630 | if (!ped || !ped->dict) { |
| 631 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 632 | error = ENOTSUP; |
| 633 | break; |
| 634 | } |
| 635 | |
| 636 | if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) { |
| 637 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 638 | error = EINVAL; |
| 639 | break; |
| 640 | } |
| 641 | |
| 642 | if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) { |
| 643 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 644 | error = EBUSY; |
| 645 | break; |
| 646 | } |
| 647 | |
| 648 | ped->flags |= SYSMON_POWER_DICTIONARY_BUSY; |
| 649 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 650 | |
| 651 | /* |
| 652 | * Send it now. |
| 653 | */ |
| 654 | error = prop_dictionary_copyout_ioctl(plist, |
| 655 | cmd, |
| 656 | ped->dict); |
| 657 | |
| 658 | /* |
| 659 | * Remove the dictionary now that we don't need it. |
| 660 | */ |
| 661 | mutex_enter(&sysmon_power_event_queue_mtx); |
| 662 | ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY; |
| 663 | ped->flags &= ~SYSMON_POWER_DICTIONARY_READY; |
| 664 | SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head); |
| 665 | mutex_exit(&sysmon_power_event_queue_mtx); |
| 666 | sysmon_power_destroy_dictionary(ped); |
| 667 | |
| 668 | break; |
| 669 | } |
| 670 | default: |
| 671 | error = ENOTTY; |
| 672 | } |
| 673 | |
| 674 | return error; |
| 675 | } |
| 676 | |
| 677 | /* |
| 678 | * sysmon_power_make_dictionary: |
| 679 | * |
| 680 | * Adds the properties for an event in a dictionary. |
| 681 | */ |
| 682 | int |
| 683 | sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data, |
| 684 | int event, int type) |
| 685 | { |
| 686 | int i; |
| 687 | |
| 688 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
| 689 | |
| 690 | switch (type) { |
| 691 | /* |
| 692 | * create the dictionary for a power switch event. |
| 693 | */ |
| 694 | case POWER_EVENT_SWITCH_STATE_CHANGE: |
| 695 | { |
| 696 | const struct power_event_description *peevent = |
| 697 | pswitch_event_desc; |
| 698 | const struct power_event_description *petype = |
| 699 | pswitch_type_desc; |
| 700 | struct sysmon_pswitch *smpsw = |
| 701 | (struct sysmon_pswitch *)power_data; |
| 702 | const char *pwrtype = "pswitch" ; |
| 703 | |
| 704 | #define SETPROP(key, str) \ |
| 705 | do { \ |
| 706 | if ((str) != NULL && !prop_dictionary_set_cstring(dict, \ |
| 707 | (key), \ |
| 708 | (str))) { \ |
| 709 | printf("%s: failed to set %s\n", __func__, (str)); \ |
| 710 | return EINVAL; \ |
| 711 | } \ |
| 712 | } while (/* CONSTCOND */ 0) |
| 713 | |
| 714 | |
| 715 | SETPROP("driver-name" , smpsw->smpsw_name); |
| 716 | |
| 717 | for (i = 0; peevent[i].type != -1; i++) |
| 718 | if (peevent[i].type == event) |
| 719 | break; |
| 720 | |
| 721 | SETPROP("powerd-event-name" , peevent[i].desc); |
| 722 | |
| 723 | for (i = 0; petype[i].type != -1; i++) |
| 724 | if (petype[i].type == smpsw->smpsw_type) |
| 725 | break; |
| 726 | |
| 727 | SETPROP("powerd-script-name" , petype[i].desc); |
| 728 | SETPROP("power-type" , pwrtype); |
| 729 | break; |
| 730 | } |
| 731 | /* |
| 732 | * create a dictionary for power envsys event. |
| 733 | */ |
| 734 | case POWER_EVENT_ENVSYS_STATE_CHANGE: |
| 735 | { |
| 736 | const struct power_event_description *peevent = |
| 737 | penvsys_event_desc; |
| 738 | const struct power_event_description *petype = |
| 739 | penvsys_type_desc; |
| 740 | struct penvsys_state *pes = |
| 741 | (struct penvsys_state *)power_data; |
| 742 | const char *pwrtype = "envsys" ; |
| 743 | |
| 744 | SETPROP("driver-name" , pes->pes_dvname); |
| 745 | SETPROP("sensor-name" , pes->pes_sensname); |
| 746 | SETPROP("state-description" , pes->pes_statedesc); |
| 747 | |
| 748 | for (i = 0; peevent[i].type != -1; i++) |
| 749 | if (peevent[i].type == event) |
| 750 | break; |
| 751 | |
| 752 | SETPROP("powerd-event-name" , peevent[i].desc); |
| 753 | |
| 754 | for (i = 0; petype[i].type != -1; i++) |
| 755 | if (petype[i].type == pes->pes_type) |
| 756 | break; |
| 757 | |
| 758 | SETPROP("powerd-script-name" , petype[i].desc); |
| 759 | SETPROP("power-type" , pwrtype); |
| 760 | break; |
| 761 | } |
| 762 | default: |
| 763 | return ENOTSUP; |
| 764 | } |
| 765 | |
| 766 | return 0; |
| 767 | } |
| 768 | |
| 769 | /* |
| 770 | * sysmon_power_destroy_dictionary: |
| 771 | * |
| 772 | * Destroys a power_event_dictionary object and all its |
| 773 | * properties in the dictionary. |
| 774 | */ |
| 775 | static void |
| 776 | sysmon_power_destroy_dictionary(struct power_event_dictionary *ped) |
| 777 | { |
| 778 | prop_object_iterator_t iter; |
| 779 | prop_object_t obj; |
| 780 | |
| 781 | KASSERT(ped != NULL); |
| 782 | KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0); |
| 783 | |
| 784 | iter = prop_dictionary_iterator(ped->dict); |
| 785 | if (iter == NULL) |
| 786 | return; |
| 787 | |
| 788 | while ((obj = prop_object_iterator_next(iter)) != NULL) { |
| 789 | prop_dictionary_remove(ped->dict, |
| 790 | prop_dictionary_keysym_cstring_nocopy(obj)); |
| 791 | prop_object_iterator_reset(iter); |
| 792 | } |
| 793 | |
| 794 | prop_object_iterator_release(iter); |
| 795 | prop_object_release(ped->dict); |
| 796 | |
| 797 | kmem_free(ped, sizeof(*ped)); |
| 798 | } |
| 799 | |
| 800 | /* |
| 801 | * sysmon_power_settype: |
| 802 | * |
| 803 | * Sets the back-end power management type. This information can |
| 804 | * be used by the power management daemon. |
| 805 | */ |
| 806 | void |
| 807 | sysmon_power_settype(const char *type) |
| 808 | { |
| 809 | |
| 810 | /* |
| 811 | * Don't bother locking this; it's going to be set |
| 812 | * during autoconfiguration, and then only read from |
| 813 | * then on. |
| 814 | */ |
| 815 | (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type)); |
| 816 | } |
| 817 | |
| 818 | #define PENVSYS_SHOWSTATE(str) \ |
| 819 | do { \ |
| 820 | printf("%s: %s limit on '%s'\n", \ |
| 821 | pes->pes_dvname, (str), pes->pes_sensname); \ |
| 822 | } while (/* CONSTCOND */ 0) |
| 823 | |
| 824 | /* |
| 825 | * sysmon_penvsys_event: |
| 826 | * |
| 827 | * Puts an event onto the sysmon power queue and sends the |
| 828 | * appropriate event if the daemon is running, otherwise a |
| 829 | * message is shown. |
| 830 | */ |
| 831 | void |
| 832 | sysmon_penvsys_event(struct penvsys_state *pes, int event) |
| 833 | { |
| 834 | struct power_event_dictionary *ped; |
| 835 | const char *mystr = NULL; |
| 836 | |
| 837 | KASSERT(pes != NULL); |
| 838 | |
| 839 | rnd_add_uint32(&sysmon_rndsource, pes->pes_type); |
| 840 | |
| 841 | if (sysmon_power_daemon != NULL) { |
| 842 | /* |
| 843 | * Create a dictionary for the new event. |
| 844 | */ |
| 845 | ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); |
| 846 | if (!ped) |
| 847 | return; |
| 848 | ped->dict = prop_dictionary_create(); |
| 849 | |
| 850 | if (sysmon_power_daemon_task(ped, pes, event) == 0) |
| 851 | return; |
| 852 | /* We failed */ |
| 853 | prop_object_release(ped->dict); |
| 854 | kmem_free(ped, sizeof(*ped)); |
| 855 | } |
| 856 | |
| 857 | switch (pes->pes_type) { |
| 858 | case PENVSYS_TYPE_BATTERY: |
| 859 | switch (event) { |
| 860 | case PENVSYS_EVENT_LOW_POWER: |
| 861 | printf("sysmon: LOW POWER! SHUTTING DOWN.\n" ); |
| 862 | cpu_reboot(RB_POWERDOWN, NULL); |
| 863 | break; |
| 864 | case PENVSYS_EVENT_STATE_CHANGED: |
| 865 | printf("%s: state changed on '%s' to '%s'\n" , |
| 866 | pes->pes_dvname, pes->pes_sensname, |
| 867 | pes->pes_statedesc); |
| 868 | break; |
| 869 | case PENVSYS_EVENT_BATT_CRIT: |
| 870 | mystr = "critical capacity" ; |
| 871 | PENVSYS_SHOWSTATE(mystr); |
| 872 | break; |
| 873 | case PENVSYS_EVENT_BATT_WARN: |
| 874 | mystr = "warning capacity" ; |
| 875 | PENVSYS_SHOWSTATE(mystr); |
| 876 | break; |
| 877 | case PENVSYS_EVENT_BATT_HIGH: |
| 878 | mystr = "high capacity" ; |
| 879 | PENVSYS_SHOWSTATE(mystr); |
| 880 | break; |
| 881 | case PENVSYS_EVENT_BATT_MAX: |
| 882 | mystr = "maximum capacity" ; |
| 883 | PENVSYS_SHOWSTATE(mystr); |
| 884 | break; |
| 885 | case PENVSYS_EVENT_NORMAL: |
| 886 | printf("%s: normal capacity on '%s'\n" , |
| 887 | pes->pes_dvname, pes->pes_sensname); |
| 888 | break; |
| 889 | } |
| 890 | break; |
| 891 | case PENVSYS_TYPE_FAN: |
| 892 | case PENVSYS_TYPE_INDICATOR: |
| 893 | case PENVSYS_TYPE_TEMP: |
| 894 | case PENVSYS_TYPE_POWER: |
| 895 | case PENVSYS_TYPE_RESISTANCE: |
| 896 | case PENVSYS_TYPE_VOLTAGE: |
| 897 | switch (event) { |
| 898 | case PENVSYS_EVENT_CRITICAL: |
| 899 | mystr = "critical" ; |
| 900 | PENVSYS_SHOWSTATE(mystr); |
| 901 | break; |
| 902 | case PENVSYS_EVENT_CRITOVER: |
| 903 | mystr = "critical over" ; |
| 904 | PENVSYS_SHOWSTATE(mystr); |
| 905 | break; |
| 906 | case PENVSYS_EVENT_CRITUNDER: |
| 907 | mystr = "critical under" ; |
| 908 | PENVSYS_SHOWSTATE(mystr); |
| 909 | break; |
| 910 | case PENVSYS_EVENT_WARNOVER: |
| 911 | mystr = "warning over" ; |
| 912 | PENVSYS_SHOWSTATE(mystr); |
| 913 | break; |
| 914 | case PENVSYS_EVENT_WARNUNDER: |
| 915 | mystr = "warning under" ; |
| 916 | PENVSYS_SHOWSTATE(mystr); |
| 917 | break; |
| 918 | case PENVSYS_EVENT_NORMAL: |
| 919 | printf("%s: normal state on '%s'\n" , |
| 920 | pes->pes_dvname, pes->pes_sensname); |
| 921 | break; |
| 922 | default: |
| 923 | printf("%s: unknown event\n" , __func__); |
| 924 | } |
| 925 | break; |
| 926 | case PENVSYS_TYPE_DRIVE: |
| 927 | switch (event) { |
| 928 | case PENVSYS_EVENT_STATE_CHANGED: |
| 929 | printf("%s: state changed on '%s' to '%s'\n" , |
| 930 | pes->pes_dvname, pes->pes_sensname, |
| 931 | pes->pes_statedesc); |
| 932 | break; |
| 933 | case PENVSYS_EVENT_NORMAL: |
| 934 | printf("%s: normal state on '%s' (%s)\n" , |
| 935 | pes->pes_dvname, pes->pes_sensname, |
| 936 | pes->pes_statedesc); |
| 937 | break; |
| 938 | } |
| 939 | break; |
| 940 | default: |
| 941 | printf("%s: unknown power type\n" , __func__); |
| 942 | break; |
| 943 | } |
| 944 | } |
| 945 | |
| 946 | /* |
| 947 | * sysmon_pswitch_register: |
| 948 | * |
| 949 | * Register a power switch device. |
| 950 | */ |
| 951 | int |
| 952 | sysmon_pswitch_register(struct sysmon_pswitch *smpsw) |
| 953 | { |
| 954 | (void)RUN_ONCE(&once_power, power_preinit); |
| 955 | |
| 956 | return 0; |
| 957 | } |
| 958 | |
| 959 | /* |
| 960 | * sysmon_pswitch_unregister: |
| 961 | * |
| 962 | * Unregister a power switch device. |
| 963 | */ |
| 964 | void |
| 965 | sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) |
| 966 | { |
| 967 | /* nada */ |
| 968 | } |
| 969 | |
| 970 | /* |
| 971 | * sysmon_pswitch_event: |
| 972 | * |
| 973 | * Register an event on a power switch device. |
| 974 | */ |
| 975 | void |
| 976 | sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) |
| 977 | { |
| 978 | struct power_event_dictionary *ped = NULL; |
| 979 | |
| 980 | KASSERT(smpsw != NULL); |
| 981 | |
| 982 | /* |
| 983 | * For pnp specific events, we don't care if the power daemon |
| 984 | * is running or not |
| 985 | */ |
| 986 | if (smpsw->smpsw_type == PSWITCH_TYPE_LID) { |
| 987 | switch (event) { |
| 988 | case PSWITCH_EVENT_PRESSED: |
| 989 | pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE); |
| 990 | break; |
| 991 | case PSWITCH_EVENT_RELEASED: |
| 992 | pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); |
| 993 | break; |
| 994 | default: |
| 995 | break; |
| 996 | } |
| 997 | } |
| 998 | |
| 999 | if (sysmon_power_daemon != NULL) { |
| 1000 | /* |
| 1001 | * Create a new dictionary for the event. |
| 1002 | */ |
| 1003 | ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); |
| 1004 | if (!ped) |
| 1005 | return; |
| 1006 | ped->dict = prop_dictionary_create(); |
| 1007 | |
| 1008 | if (sysmon_power_daemon_task(ped, smpsw, event) == 0) |
| 1009 | return; |
| 1010 | /* We failed */ |
| 1011 | prop_object_release(ped->dict); |
| 1012 | kmem_free(ped, sizeof(*ped)); |
| 1013 | } |
| 1014 | |
| 1015 | switch (smpsw->smpsw_type) { |
| 1016 | case PSWITCH_TYPE_POWER: |
| 1017 | if (event != PSWITCH_EVENT_PRESSED) { |
| 1018 | /* just ignore it */ |
| 1019 | return; |
| 1020 | } |
| 1021 | |
| 1022 | /* |
| 1023 | * Attempt a somewhat graceful shutdown of the system, |
| 1024 | * as if the user has issued a reboot(2) call with |
| 1025 | * RB_POWERDOWN. |
| 1026 | */ |
| 1027 | printf("%s: power button pressed, shutting down!\n" , |
| 1028 | smpsw->smpsw_name); |
| 1029 | cpu_reboot(RB_POWERDOWN, NULL); |
| 1030 | break; |
| 1031 | |
| 1032 | case PSWITCH_TYPE_RESET: |
| 1033 | if (event != PSWITCH_EVENT_PRESSED) { |
| 1034 | /* just ignore it */ |
| 1035 | return; |
| 1036 | } |
| 1037 | |
| 1038 | /* |
| 1039 | * Attempt a somewhat graceful reboot of the system, |
| 1040 | * as if the user had issued a reboot(2) call. |
| 1041 | */ |
| 1042 | printf("%s: reset button pressed, rebooting!\n" , |
| 1043 | smpsw->smpsw_name); |
| 1044 | cpu_reboot(0, NULL); |
| 1045 | break; |
| 1046 | |
| 1047 | case PSWITCH_TYPE_SLEEP: |
| 1048 | if (event != PSWITCH_EVENT_PRESSED) { |
| 1049 | /* just ignore it */ |
| 1050 | return; |
| 1051 | } |
| 1052 | |
| 1053 | /* |
| 1054 | * Try to enter a "sleep" state. |
| 1055 | */ |
| 1056 | /* XXX */ |
| 1057 | printf("%s: sleep button pressed.\n" , smpsw->smpsw_name); |
| 1058 | break; |
| 1059 | |
| 1060 | case PSWITCH_TYPE_HOTKEY: |
| 1061 | /* |
| 1062 | * Eat up the event, there's nothing we can do |
| 1063 | */ |
| 1064 | break; |
| 1065 | |
| 1066 | case PSWITCH_TYPE_LID: |
| 1067 | switch (event) { |
| 1068 | case PSWITCH_EVENT_PRESSED: |
| 1069 | /* |
| 1070 | * Try to enter a "standby" state. |
| 1071 | */ |
| 1072 | /* XXX */ |
| 1073 | printf("%s: lid closed.\n" , smpsw->smpsw_name); |
| 1074 | break; |
| 1075 | |
| 1076 | case PSWITCH_EVENT_RELEASED: |
| 1077 | /* |
| 1078 | * Come out of "standby" state. |
| 1079 | */ |
| 1080 | /* XXX */ |
| 1081 | printf("%s: lid opened.\n" , smpsw->smpsw_name); |
| 1082 | break; |
| 1083 | |
| 1084 | default: |
| 1085 | printf("%s: unknown lid switch event: %d\n" , |
| 1086 | smpsw->smpsw_name, event); |
| 1087 | } |
| 1088 | break; |
| 1089 | |
| 1090 | case PSWITCH_TYPE_ACADAPTER: |
| 1091 | switch (event) { |
| 1092 | case PSWITCH_EVENT_PRESSED: |
| 1093 | /* |
| 1094 | * Come out of power-save state. |
| 1095 | */ |
| 1096 | aprint_normal("%s: AC adapter online.\n" , |
| 1097 | smpsw->smpsw_name); |
| 1098 | break; |
| 1099 | |
| 1100 | case PSWITCH_EVENT_RELEASED: |
| 1101 | /* |
| 1102 | * Try to enter a power-save state. |
| 1103 | */ |
| 1104 | aprint_normal("%s: AC adapter offline.\n" , |
| 1105 | smpsw->smpsw_name); |
| 1106 | break; |
| 1107 | } |
| 1108 | break; |
| 1109 | |
| 1110 | } |
| 1111 | } |
| 1112 | |
| 1113 | static |
| 1114 | int |
| 1115 | sysmon_power_modcmd(modcmd_t cmd, void *arg) |
| 1116 | { |
| 1117 | int ret; |
| 1118 | |
| 1119 | switch (cmd) { |
| 1120 | case MODULE_CMD_INIT: |
| 1121 | ret = sysmon_power_init(); |
| 1122 | break; |
| 1123 | |
| 1124 | case MODULE_CMD_FINI: |
| 1125 | ret = sysmon_power_fini(); |
| 1126 | break; |
| 1127 | |
| 1128 | case MODULE_CMD_STAT: |
| 1129 | default: |
| 1130 | ret = ENOTTY; |
| 1131 | } |
| 1132 | |
| 1133 | return ret; |
| 1134 | } |
| 1135 | |
| 1136 | |