1 /* $NetBSD: sysmon_envsys_events.c,v 1.79 2010/02/05 19:22:25 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, 2008 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 * sysmon_envsys(9) events framework. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.79 2010/02/05 19:22:25 jruoho Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/conf.h> 38 #include <sys/errno.h> 39 #include <sys/kernel.h> 40 #include <sys/systm.h> 41 #include <sys/proc.h> 42 #include <sys/mutex.h> 43 #include <sys/kmem.h> 44 #include <sys/callout.h> 45 46 /* #define ENVSYS_DEBUG */ 47 48 #include <dev/sysmon/sysmonvar.h> 49 #include <dev/sysmon/sysmon_envsysvar.h> 50 51 struct sme_sensor_event { 52 int state; 53 int event; 54 }; 55 56 static const struct sme_sensor_event sme_sensor_event[] = { 57 { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL }, 58 { ENVSYS_SCRITOVER, PENVSYS_EVENT_CRITOVER }, 59 { ENVSYS_SCRITUNDER, PENVSYS_EVENT_CRITUNDER }, 60 { ENVSYS_SWARNOVER, PENVSYS_EVENT_WARNOVER }, 61 { ENVSYS_SWARNUNDER, PENVSYS_EVENT_WARNUNDER }, 62 { ENVSYS_BATTERY_CAPACITY_NORMAL, PENVSYS_EVENT_NORMAL }, 63 { ENVSYS_BATTERY_CAPACITY_WARNING, PENVSYS_EVENT_BATT_WARN }, 64 { ENVSYS_BATTERY_CAPACITY_CRITICAL, PENVSYS_EVENT_BATT_CRIT }, 65 { -1, -1 } 66 }; 67 68 static bool sysmon_low_power; 69 70 #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz) 71 72 static bool sme_event_check_low_power(void); 73 static bool sme_battery_check(void); 74 static bool sme_battery_critical(envsys_data_t *); 75 static bool sme_acadapter_check(void); 76 77 /* 78 * sme_event_register: 79 * 80 * + Registers a new sysmon envsys event or updates any event 81 * already in the queue. 82 */ 83 int 84 sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata, 85 struct sysmon_envsys *sme, sysmon_envsys_lim_t *lims, 86 int crittype, int powertype) 87 { 88 sme_event_t *see = NULL, *osee = NULL; 89 prop_object_t obj; 90 int error = 0; 91 const char *objkey; 92 93 KASSERT(sdict != NULL); 94 KASSERT(edata != NULL); 95 KASSERT(sme != NULL); 96 KASSERT(lims != NULL); 97 98 /* 99 * Some validation first for limit-checking events 100 * 101 * Capacity limits are permitted only if the sensor has the 102 * ENVSYS_FPERCENT flag set. 103 * Value limits are permitted only if the ENVSYS_FPERCENT 104 * flag is not set and the units is not ENVSYS_INDICATOR. 105 */ 106 107 DPRINTF(("%s: units %d lim-flags 0x%04x edata-flags 0x%04x\n", 108 __func__, edata->units, lims->sel_flags, edata->flags)); 109 110 if ((lims->sel_flags & PROP_VAL_LIMITS) && 111 ((edata->flags & ENVSYS_FPERCENT) || 112 (edata->units == ENVSYS_INDICATOR))) 113 return ENOTSUP; 114 if ((lims->sel_flags & PROP_CAP_LIMITS) && 115 !(edata->flags & ENVSYS_FPERCENT)) 116 return ENOTSUP; 117 118 /* 119 * check if the event is already on the list and return 120 * EEXIST if value provided hasn't been changed. 121 */ 122 mutex_enter(&sme->sme_mtx); 123 LIST_FOREACH(osee, &sme->sme_events_list, see_list) { 124 if (strcmp(edata->desc, osee->see_pes.pes_sensname) != 0) 125 continue; 126 if (crittype != osee->see_type) 127 continue; 128 129 /* 130 * We found an existing event for this sensor. Make 131 * sure it references the correct edata 132 */ 133 KASSERT(edata == osee->see_edata); 134 135 DPRINTF(("%s: dev %s sensor %s: event type %d exists\n", 136 __func__, sme->sme_name, edata->desc, crittype)); 137 138 see = osee; 139 if (lims->sel_flags & PROP_CRITMAX) { 140 if (lims->sel_critmax == edata->limits.sel_critmax) { 141 DPRINTF(("%s: type=%d (critmax exists)\n", 142 __func__, crittype)); 143 error = EEXIST; 144 lims->sel_flags &= ~PROP_CRITMAX; 145 } 146 } 147 if (lims->sel_flags & PROP_WARNMAX) { 148 if (lims->sel_warnmax == edata->limits.sel_warnmax) { 149 DPRINTF(("%s: warnmax exists\n", __func__)); 150 error = EEXIST; 151 lims->sel_flags &= ~PROP_WARNMAX; 152 } 153 } 154 if (lims->sel_flags & (PROP_WARNMIN | PROP_BATTWARN)) { 155 if (lims->sel_warnmin == edata->limits.sel_warnmin) { 156 DPRINTF(("%s: warnmin exists\n", __func__)); 157 error = EEXIST; 158 lims->sel_flags &= 159 ~(PROP_WARNMIN | PROP_BATTWARN); 160 } 161 } 162 if (lims->sel_flags & (PROP_CRITMIN | PROP_BATTCAP)) { 163 if (lims->sel_critmin == edata->limits.sel_critmin) { 164 DPRINTF(("%s: critmin exists\n", __func__)); 165 error = EEXIST; 166 lims->sel_flags &= 167 ~(PROP_CRITMIN | PROP_BATTCAP); 168 } 169 } 170 break; 171 } 172 if (see == NULL) { 173 /* 174 * New event requested - allocate a sysmon_envsys event. 175 */ 176 see = kmem_zalloc(sizeof(*see), KM_SLEEP); 177 if (see == NULL) 178 return ENOMEM; 179 180 DPRINTF(("%s: dev %s sensor %s: new event\n", 181 __func__, sme->sme_name, edata->desc)); 182 183 see->see_type = crittype; 184 see->see_sme = sme; 185 see->see_edata = edata; 186 187 /* Initialize sensor type and previously-sent state */ 188 189 see->see_pes.pes_type = powertype; 190 191 switch (crittype) { 192 case PENVSYS_EVENT_LIMITS: 193 see->see_evsent = ENVSYS_SVALID; 194 break; 195 case PENVSYS_EVENT_CAPACITY: 196 see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL; 197 break; 198 case PENVSYS_EVENT_STATE_CHANGED: 199 if (edata->units == ENVSYS_BATTERY_CAPACITY) 200 see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL; 201 else if (edata->units == ENVSYS_DRIVE) 202 see->see_evsent = ENVSYS_DRIVE_EMPTY; 203 else 204 panic("%s: bad units for " 205 "PENVSYS_EVENT_STATE_CHANGED", __func__); 206 break; 207 case PENVSYS_EVENT_CRITICAL: 208 default: 209 see->see_evsent = 0; 210 break; 211 } 212 213 (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name, 214 sizeof(see->see_pes.pes_dvname)); 215 (void)strlcpy(see->see_pes.pes_sensname, edata->desc, 216 sizeof(see->see_pes.pes_sensname)); 217 } 218 219 /* 220 * Limit operation requested. 221 */ 222 if (lims->sel_flags & PROP_CRITMAX) { 223 objkey = "critical-max"; 224 obj = prop_dictionary_get(sdict, objkey); 225 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 226 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 227 __func__, sme->sme_name, objkey)); 228 error = ENOTSUP; 229 } else { 230 edata->limits.sel_critmax = lims->sel_critmax; 231 error = sme_sensor_upint32(sdict, objkey, 232 lims->sel_critmax); 233 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 234 "(%s updated)\n", __func__, sme->sme_name, 235 edata->desc, crittype, objkey)); 236 } 237 if (error && error != EEXIST) 238 goto out; 239 edata->upropset |= PROP_CRITMAX; 240 } 241 242 if (lims->sel_flags & PROP_WARNMAX) { 243 objkey = "warning-max"; 244 obj = prop_dictionary_get(sdict, objkey); 245 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 246 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 247 __func__, sme->sme_name, objkey)); 248 error = ENOTSUP; 249 } else { 250 edata->limits.sel_warnmax = lims->sel_warnmax; 251 error = sme_sensor_upint32(sdict, objkey, 252 lims->sel_warnmax); 253 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 254 "(%s updated)\n", __func__, sme->sme_name, 255 edata->desc, crittype, objkey)); 256 } 257 if (error && error != EEXIST) 258 goto out; 259 edata->upropset |= PROP_WARNMAX; 260 } 261 262 if (lims->sel_flags & PROP_WARNMIN) { 263 objkey = "warning-min"; 264 obj = prop_dictionary_get(sdict, objkey); 265 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 266 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 267 __func__, sme->sme_name, objkey)); 268 error = ENOTSUP; 269 } else { 270 edata->limits.sel_warnmin = lims->sel_warnmin; 271 error = sme_sensor_upint32(sdict, objkey, 272 lims->sel_warnmin); 273 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 274 "(%s updated)\n", __func__, sme->sme_name, 275 edata->desc, crittype, objkey)); 276 } 277 if (error && error != EEXIST) 278 goto out; 279 edata->upropset |= PROP_WARNMIN; 280 } 281 282 if (lims->sel_flags & PROP_CRITMIN) { 283 objkey = "critical-min"; 284 obj = prop_dictionary_get(sdict, objkey); 285 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 286 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 287 __func__, sme->sme_name, objkey)); 288 error = ENOTSUP; 289 } else { 290 edata->limits.sel_critmin = lims->sel_critmin; 291 error = sme_sensor_upint32(sdict, objkey, 292 lims->sel_critmin); 293 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 294 "(%s updated)\n", __func__, sme->sme_name, 295 edata->desc, crittype, objkey)); 296 } 297 if (error && error != EEXIST) 298 goto out; 299 edata->upropset |= PROP_CRITMIN; 300 } 301 302 if (lims->sel_flags & PROP_BATTWARN) { 303 objkey = "warning-capacity"; 304 obj = prop_dictionary_get(sdict, objkey); 305 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 306 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 307 __func__, sme->sme_name, objkey)); 308 error = ENOTSUP; 309 } else { 310 edata->limits.sel_warnmin = lims->sel_warnmin; 311 error = sme_sensor_upint32(sdict, objkey, 312 lims->sel_warnmin); 313 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 314 "(%s updated)\n", __func__, sme->sme_name, 315 edata->desc, crittype, objkey)); 316 } 317 if (error && error != EEXIST) 318 goto out; 319 edata->upropset |= PROP_BATTWARN; 320 } 321 322 if (lims->sel_flags & PROP_BATTCAP) { 323 objkey = "critical-capacity"; 324 obj = prop_dictionary_get(sdict, objkey); 325 if (obj && prop_object_type(obj) != PROP_TYPE_NUMBER) { 326 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n", 327 __func__, sme->sme_name, objkey)); 328 error = ENOTSUP; 329 } else { 330 edata->limits.sel_critmin = lims->sel_critmin; 331 error = sme_sensor_upint32(sdict, objkey, 332 lims->sel_critmin); 333 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 334 "(%s updated)\n", __func__, sme->sme_name, 335 edata->desc, crittype, objkey)); 336 } 337 if (error && error != EEXIST) 338 goto out; 339 edata->upropset |= PROP_BATTCAP; 340 } 341 342 if (lims->sel_flags & PROP_DRIVER_LIMITS) 343 edata->upropset |= PROP_DRIVER_LIMITS; 344 else 345 edata->upropset &= ~PROP_DRIVER_LIMITS; 346 347 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d " 348 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32 349 " critmax=%" PRIu32 " props 0x%04x)\n", __func__, 350 see->see_sme->sme_name, see->see_pes.pes_sensname, 351 edata->sensor, see->see_type, edata->limits.sel_critmin, 352 edata->limits.sel_warnmin, edata->limits.sel_warnmax, 353 edata->limits.sel_critmax, edata->upropset)); 354 /* 355 * Initialize the events framework if it wasn't initialized before. 356 */ 357 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) 358 error = sme_events_init(sme); 359 360 /* 361 * If driver requested notification, advise it of new 362 * limit values 363 */ 364 if (sme->sme_set_limits) { 365 edata->limits.sel_flags = edata->upropset & PROP_LIMITS; 366 (*sme->sme_set_limits)(sme, edata, &(edata->limits)); 367 } 368 369 out: 370 if ((error == 0 || error == EEXIST) && osee == NULL) 371 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list); 372 373 mutex_exit(&sme->sme_mtx); 374 375 return error; 376 } 377 378 /* 379 * sme_event_unregister_all: 380 * 381 * + Unregisters all events associated with a sysmon envsys device. 382 */ 383 void 384 sme_event_unregister_all(struct sysmon_envsys *sme) 385 { 386 sme_event_t *see; 387 int evcounter = 0; 388 389 KASSERT(sme != NULL); 390 391 mutex_enter(&sme->sme_mtx); 392 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 393 while (see->see_flags & SEE_EVENT_WORKING) 394 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 395 396 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) 397 evcounter++; 398 } 399 400 DPRINTF(("%s: total events %d (%s)\n", __func__, 401 evcounter, sme->sme_name)); 402 403 while ((see = LIST_FIRST(&sme->sme_events_list))) { 404 if (evcounter == 0) 405 break; 406 407 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) { 408 LIST_REMOVE(see, see_list); 409 DPRINTF(("%s: event %s %d removed (%s)\n", __func__, 410 see->see_pes.pes_sensname, see->see_type, 411 sme->sme_name)); 412 kmem_free(see, sizeof(*see)); 413 evcounter--; 414 } 415 } 416 417 if (LIST_EMPTY(&sme->sme_events_list)) 418 if (sme->sme_flags & SME_CALLOUT_INITIALIZED) 419 sme_events_destroy(sme); 420 mutex_exit(&sme->sme_mtx); 421 } 422 423 /* 424 * sme_event_unregister: 425 * 426 * + Unregisters an event from the specified sysmon envsys device. 427 */ 428 int 429 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type) 430 { 431 sme_event_t *see; 432 bool found = false; 433 434 KASSERT(sensor != NULL); 435 436 mutex_enter(&sme->sme_mtx); 437 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 438 if (strcmp(see->see_pes.pes_sensname, sensor) == 0) { 439 if (see->see_type == type) { 440 found = true; 441 break; 442 } 443 } 444 } 445 446 if (!found) { 447 mutex_exit(&sme->sme_mtx); 448 return EINVAL; 449 } 450 451 /* 452 * Wait for the event to finish its work, remove from the list 453 * and release resouces. 454 */ 455 while (see->see_flags & SEE_EVENT_WORKING) 456 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 457 458 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n", 459 __func__, see->see_pes.pes_dvname, sensor, type)); 460 LIST_REMOVE(see, see_list); 461 /* 462 * So the events list is empty, we'll do the following: 463 * 464 * - stop and destroy the callout. 465 * - destroy the workqueue. 466 */ 467 if (LIST_EMPTY(&sme->sme_events_list)) 468 sme_events_destroy(sme); 469 mutex_exit(&sme->sme_mtx); 470 471 kmem_free(see, sizeof(*see)); 472 return 0; 473 } 474 475 /* 476 * sme_event_drvadd: 477 * 478 * + Registers a new event for a device that had enabled any of 479 * the monitoring flags in the driver. 480 */ 481 void 482 sme_event_drvadd(void *arg) 483 { 484 sme_event_drv_t *sed_t = arg; 485 sysmon_envsys_lim_t lims; 486 int error = 0; 487 488 KASSERT(sed_t != NULL); 489 490 #define SEE_REGEVENT(a, b, c) \ 491 do { \ 492 if (sed_t->sed_edata->flags & (a)) { \ 493 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \ 494 \ 495 error = sme_event_register(sed_t->sed_sdict, \ 496 sed_t->sed_edata, \ 497 sed_t->sed_sme, \ 498 &lims, \ 499 (b), \ 500 sed_t->sed_powertype); \ 501 if (error && error != EEXIST) \ 502 printf("%s: failed to add event! " \ 503 "error=%d sensor=%s event=%s\n", \ 504 __func__, error, \ 505 sed_t->sed_edata->desc, (c)); \ 506 else { \ 507 (void)strlcat(str, (c), sizeof(str)); \ 508 prop_dictionary_set_bool(sed_t->sed_sdict, \ 509 str, \ 510 true); \ 511 } \ 512 } \ 513 } while (/* CONSTCOND */ 0) 514 515 /* 516 * If driver provides a method to retrieve its internal limit 517 * values, call it and use those returned values as initial 518 * limits for event monitoring. 519 */ 520 lims.sel_flags = 0; 521 if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS) 522 if (sed_t->sed_sme->sme_get_limits) 523 (*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme, 524 sed_t->sed_edata, 525 &lims); 526 /* 527 * If no values returned, don't create the event monitor at 528 * this time. We'll get another chance later when the user 529 * provides us with limits. 530 */ 531 if (lims.sel_flags == 0) 532 sed_t->sed_edata->flags &= ~ENVSYS_FMONLIMITS; 533 534 /* 535 * If driver doesn't provide a way to "absorb" user-specified 536 * limit values, we must monitor all limits ourselves 537 */ 538 else if (sed_t->sed_sme->sme_set_limits == NULL) 539 lims.sel_flags &= ~PROP_DRIVER_LIMITS; 540 541 /* Register the events that were specified */ 542 543 SEE_REGEVENT(ENVSYS_FMONCRITICAL, 544 PENVSYS_EVENT_CRITICAL, 545 "critical"); 546 547 SEE_REGEVENT(ENVSYS_FMONSTCHANGED, 548 PENVSYS_EVENT_STATE_CHANGED, 549 "state-changed"); 550 551 SEE_REGEVENT(ENVSYS_FMONLIMITS, 552 PENVSYS_EVENT_LIMITS, 553 "hw-range-limits"); 554 555 /* 556 * we are done, free memory now. 557 */ 558 kmem_free(sed_t, sizeof(*sed_t)); 559 } 560 561 /* 562 * sme_events_init: 563 * 564 * + Initialize the events framework for this device. 565 */ 566 int 567 sme_events_init(struct sysmon_envsys *sme) 568 { 569 int error = 0; 570 uint64_t timo; 571 572 KASSERT(sme != NULL); 573 KASSERT(mutex_owned(&sme->sme_mtx)); 574 575 if (sme->sme_events_timeout) 576 timo = sme->sme_events_timeout * hz; 577 else 578 timo = SME_EVTIMO; 579 580 error = workqueue_create(&sme->sme_wq, sme->sme_name, 581 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE); 582 if (error) 583 return error; 584 585 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 586 callout_init(&sme->sme_callout, CALLOUT_MPSAFE); 587 callout_setfunc(&sme->sme_callout, sme_events_check, sme); 588 callout_schedule(&sme->sme_callout, timo); 589 sme->sme_flags |= SME_CALLOUT_INITIALIZED; 590 DPRINTF(("%s: events framework initialized for '%s'\n", 591 __func__, sme->sme_name)); 592 593 return error; 594 } 595 596 /* 597 * sme_events_destroy: 598 * 599 * + Destroys the event framework for this device: callout 600 * stopped, workqueue destroyed and callout mutex destroyed. 601 */ 602 void 603 sme_events_destroy(struct sysmon_envsys *sme) 604 { 605 KASSERT(mutex_owned(&sme->sme_mtx)); 606 607 callout_stop(&sme->sme_callout); 608 workqueue_destroy(sme->sme_wq); 609 mutex_destroy(&sme->sme_callout_mtx); 610 callout_destroy(&sme->sme_callout); 611 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; 612 DPRINTF(("%s: events framework destroyed for '%s'\n", 613 __func__, sme->sme_name)); 614 } 615 616 /* 617 * sme_events_check: 618 * 619 * + Passes the events to the workqueue thread and stops 620 * the callout if the 'low-power' condition is triggered. 621 */ 622 void 623 sme_events_check(void *arg) 624 { 625 struct sysmon_envsys *sme = arg; 626 sme_event_t *see; 627 uint64_t timo; 628 629 KASSERT(sme != NULL); 630 631 mutex_enter(&sme->sme_callout_mtx); 632 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 633 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); 634 see->see_edata->flags |= ENVSYS_FNEED_REFRESH; 635 } 636 if (sme->sme_events_timeout) 637 timo = sme->sme_events_timeout * hz; 638 else 639 timo = SME_EVTIMO; 640 if (!sysmon_low_power) 641 callout_schedule(&sme->sme_callout, timo); 642 mutex_exit(&sme->sme_callout_mtx); 643 } 644 645 /* 646 * sme_events_worker: 647 * 648 * + workqueue thread that checks if there's a critical condition 649 * and sends an event if it was triggered. 650 */ 651 void 652 sme_events_worker(struct work *wk, void *arg) 653 { 654 const struct sme_description_table *sdt = NULL; 655 const struct sme_sensor_event *sse = sme_sensor_event; 656 sme_event_t *see = (void *)wk; 657 struct sysmon_envsys *sme = see->see_sme; 658 envsys_data_t *edata = see->see_edata; 659 int i, state = 0; 660 661 KASSERT(wk == &see->see_wk); 662 KASSERT(sme != NULL || edata != NULL); 663 664 mutex_enter(&sme->sme_mtx); 665 if ((see->see_flags & SEE_EVENT_WORKING) == 0) 666 see->see_flags |= SEE_EVENT_WORKING; 667 /* 668 * sme_events_check marks the sensors to make us refresh them here. 669 * Don't refresh if the driver uses its own method for refreshing. 670 */ 671 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 672 if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) { 673 /* refresh sensor in device */ 674 (*sme->sme_refresh)(sme, edata); 675 edata->flags &= ~ENVSYS_FNEED_REFRESH; 676 } 677 } 678 679 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d " 680 "value_cur=%d\n", __func__, sme->sme_name, edata->desc, 681 edata->sensor, see->see_type, edata->state, edata->units, 682 edata->value_cur)); 683 684 /* skip the event if current sensor is in invalid state */ 685 if (edata->state == ENVSYS_SINVALID) 686 goto out; 687 688 switch (see->see_type) { 689 /* 690 * For range limits, if the driver claims responsibility for 691 * limit/range checking, just user driver-supplied status. 692 * Else calculate our own status. Note that driver must 693 * relinquish responsibility for ALL limits if there is even 694 * one limit that it cannot handle! 695 */ 696 case PENVSYS_EVENT_LIMITS: 697 case PENVSYS_EVENT_CAPACITY: 698 #define __EXCEED_LIM(valid, lim, rel) \ 699 ((edata->limits.sel_flags & (valid)) && \ 700 (edata->value_cur rel (edata->limits.lim))) 701 702 if ((edata->limits.sel_flags & PROP_DRIVER_LIMITS) == 0) { 703 if __EXCEED_LIM(PROP_CRITMIN | PROP_BATTCAP, 704 sel_critmin, <) 705 edata->state = ENVSYS_SCRITUNDER; 706 else if __EXCEED_LIM(PROP_WARNMIN | PROP_BATTWARN, 707 sel_warnmin, <) 708 edata->state = ENVSYS_SWARNUNDER; 709 else if __EXCEED_LIM(PROP_CRITMAX, sel_critmax, >) 710 edata->state = ENVSYS_SCRITOVER; 711 else if __EXCEED_LIM(PROP_WARNMAX, sel_warnmax, >) 712 edata->state = ENVSYS_SWARNOVER; 713 else 714 edata->state = ENVSYS_SVALID; 715 } 716 #undef __EXCEED_LIM 717 718 /* 719 * Send event if state has changed 720 */ 721 if (edata->state == see->see_evsent) 722 break; 723 724 for (i = 0; sse[i].state != -1; i++) 725 if (sse[i].state == edata->state) 726 break; 727 728 if (sse[i].state == -1) 729 break; 730 731 if (edata->state == ENVSYS_SVALID) 732 sysmon_penvsys_event(&see->see_pes, 733 PENVSYS_EVENT_NORMAL); 734 else 735 sysmon_penvsys_event(&see->see_pes, sse[i].event); 736 737 see->see_evsent = edata->state; 738 739 break; 740 741 /* 742 * Send PENVSYS_EVENT_CRITICAL event if: 743 * State has gone from non-CRITICAL to CRITICAL, 744 * State remains CRITICAL and value has changed, or 745 * State has returned from CRITICAL to non-CRITICAL 746 */ 747 case PENVSYS_EVENT_CRITICAL: 748 if (edata->state == ENVSYS_SVALID && 749 see->see_evsent != 0) { 750 sysmon_penvsys_event(&see->see_pes, 751 PENVSYS_EVENT_NORMAL); 752 see->see_evsent = 0; 753 } else if (edata->state == ENVSYS_SCRITICAL && 754 see->see_evsent != edata->value_cur) { 755 sysmon_penvsys_event(&see->see_pes, 756 PENVSYS_EVENT_CRITICAL); 757 see->see_evsent = edata->value_cur; 758 } 759 break; 760 761 /* 762 * if value_cur is not normal (battery) or online (drive), 763 * send the event... 764 */ 765 case PENVSYS_EVENT_STATE_CHANGED: 766 /* 767 * the state has not been changed, just ignore the event. 768 */ 769 if (edata->value_cur == see->see_evsent) 770 break; 771 772 switch (edata->units) { 773 case ENVSYS_DRIVE: 774 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 775 state = ENVSYS_DRIVE_ONLINE; 776 break; 777 case ENVSYS_BATTERY_CAPACITY: 778 sdt = sme_get_description_table( 779 SME_DESC_BATTERY_CAPACITY); 780 state = ENVSYS_BATTERY_CAPACITY_NORMAL; 781 break; 782 default: 783 panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED", 784 __func__); 785 } 786 787 for (i = 0; sdt[i].type != -1; i++) 788 if (sdt[i].type == edata->value_cur) 789 break; 790 791 if (sdt[i].type == -1) 792 break; 793 794 /* 795 * copy current state description. 796 */ 797 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc, 798 sizeof(see->see_pes.pes_statedesc)); 799 800 if (edata->value_cur == state) 801 /* 802 * state returned to normal condition 803 */ 804 sysmon_penvsys_event(&see->see_pes, 805 PENVSYS_EVENT_NORMAL); 806 else 807 /* 808 * state changed to abnormal condition 809 */ 810 sysmon_penvsys_event(&see->see_pes, see->see_type); 811 812 see->see_evsent = edata->value_cur; 813 814 /* 815 * There's no need to continue if it's a drive sensor. 816 */ 817 if (edata->units == ENVSYS_DRIVE) 818 break; 819 820 /* 821 * Check if the system is running in low power and send the 822 * event to powerd (if running) or shutdown the system 823 * otherwise. 824 */ 825 if (!sysmon_low_power && sme_event_check_low_power()) { 826 struct penvsys_state pes; 827 828 /* 829 * Stop the callout and send the 'low-power' event. 830 */ 831 sysmon_low_power = true; 832 callout_stop(&sme->sme_callout); 833 pes.pes_type = PENVSYS_TYPE_BATTERY; 834 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); 835 } 836 break; 837 default: 838 panic("%s: invalid event type %d", __func__, see->see_type); 839 } 840 841 out: 842 see->see_flags &= ~SEE_EVENT_WORKING; 843 cv_broadcast(&sme->sme_condvar); 844 mutex_exit(&sme->sme_mtx); 845 } 846 847 /* 848 * Returns true if the system is in low power state: an AC adapter 849 * is OFF and all batteries are in LOW/CRITICAL state. 850 */ 851 static bool 852 sme_event_check_low_power(void) 853 { 854 if (!sme_acadapter_check()) 855 return false; 856 857 return sme_battery_check(); 858 } 859 860 /* 861 * Called with the sysmon_envsys device mtx held through the 862 * workqueue thread. 863 */ 864 static bool 865 sme_acadapter_check(void) 866 { 867 struct sysmon_envsys *sme; 868 envsys_data_t *edata; 869 bool dev = false, sensor = false; 870 871 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 872 if (sme->sme_class == SME_CLASS_ACADAPTER) { 873 dev = true; 874 break; 875 } 876 } 877 878 /* 879 * No AC Adapter devices were found. 880 */ 881 if (!dev) 882 return false; 883 884 /* 885 * Check if there's an AC adapter device connected. 886 */ 887 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 888 if (edata->units == ENVSYS_INDICATOR) { 889 sensor = true; 890 /* refresh current sensor */ 891 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 892 (*sme->sme_refresh)(sme, edata); 893 if (edata->value_cur) 894 return false; 895 } 896 } 897 898 if (!sensor) 899 return false; 900 901 /* 902 * AC adapter found and not connected. 903 */ 904 return true; 905 } 906 907 /* 908 * Called with the sysmon_envsys device mtx held through the 909 * workqueue thread. 910 */ 911 static bool 912 sme_battery_check(void) 913 { 914 struct sysmon_envsys *sme; 915 envsys_data_t *edata; 916 int batteriesfound = 0; 917 bool present, batterycap, batterycharge; 918 919 /* 920 * Check for battery devices and its state. 921 */ 922 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 923 if (sme->sme_class != SME_CLASS_BATTERY) 924 continue; 925 926 present = true; 927 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 928 if (edata->units == ENVSYS_INDICATOR && 929 !edata->value_cur) { 930 present = false; 931 break; 932 } 933 } 934 if (!present) 935 continue; 936 /* 937 * We've found a battery device... 938 */ 939 batteriesfound++; 940 batterycap = batterycharge = false; 941 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 942 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 943 batterycap = true; 944 if (!sme_battery_critical(edata)) 945 return false; 946 } else if (edata->units == ENVSYS_BATTERY_CHARGE) { 947 batterycharge = true; 948 if (edata->value_cur) 949 return false; 950 } 951 } 952 if (!batterycap || !batterycharge) 953 return false; 954 } 955 956 if (!batteriesfound) 957 return false; 958 959 /* 960 * All batteries in low/critical capacity and discharging. 961 */ 962 return true; 963 } 964 965 static bool 966 sme_battery_critical(envsys_data_t *edata) 967 { 968 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL || 969 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW) 970 return true; 971 972 return false; 973 } 974