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