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