1 /* $NetBSD: sysmon_envsys_events.c,v 1.71 2009/07/10 15:27:33 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.71 2009/07/10 15:27:33 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, lims->sel_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, lims->sel_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 /* 484 * If driver provides method to retrieve its internal limit 485 * values, call it. If it returns any values, set the flag 486 * PROP_DRIVER_LIMITS to indicate that the driver can process 487 * all the limits we have. (If userland limits are specified 488 * later and the driver cannot handle them, this flag will be 489 * cleared.) 490 * 491 * If the driver cannot or does not provide us with limit values 492 * we cannot monitor limits now; we get another chance to create 493 * the FMONLIMITS entry later if userland specifies some limits. 494 */ 495 lims.sel_flags = 0; 496 if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS) 497 if (sed_t->sed_sme->sme_get_limits) 498 (*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme, 499 sed_t->sed_edata, 500 &lims); 501 if (lims.sel_flags) 502 lims.sel_flags |= PROP_DRIVER_LIMITS; 503 else 504 sed_t->sed_edata->flags &= ~ENVSYS_FMONLIMITS; 505 506 /* Register the events that were specified */ 507 508 SEE_REGEVENT(ENVSYS_FMONCRITICAL, 509 PENVSYS_EVENT_CRITICAL, 510 "critical"); 511 512 SEE_REGEVENT(ENVSYS_FMONSTCHANGED, 513 PENVSYS_EVENT_STATE_CHANGED, 514 "state-changed"); 515 516 SEE_REGEVENT(ENVSYS_FMONLIMITS, 517 PENVSYS_EVENT_LIMITS, 518 "hw-range-limits"); 519 520 /* 521 * we are done, free memory now. 522 */ 523 kmem_free(sed_t, sizeof(*sed_t)); 524 } 525 526 /* 527 * sme_events_init: 528 * 529 * + Initialize the events framework for this device. 530 */ 531 int 532 sme_events_init(struct sysmon_envsys *sme) 533 { 534 int error = 0; 535 uint64_t timo; 536 537 KASSERT(sme != NULL); 538 KASSERT(mutex_owned(&sme->sme_mtx)); 539 540 if (sme->sme_events_timeout) 541 timo = sme->sme_events_timeout * hz; 542 else 543 timo = SME_EVTIMO; 544 545 error = workqueue_create(&sme->sme_wq, sme->sme_name, 546 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE); 547 if (error) 548 return error; 549 550 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 551 callout_init(&sme->sme_callout, CALLOUT_MPSAFE); 552 callout_setfunc(&sme->sme_callout, sme_events_check, sme); 553 callout_schedule(&sme->sme_callout, timo); 554 sme->sme_flags |= SME_CALLOUT_INITIALIZED; 555 DPRINTF(("%s: events framework initialized for '%s'\n", 556 __func__, sme->sme_name)); 557 558 return error; 559 } 560 561 /* 562 * sme_events_destroy: 563 * 564 * + Destroys the event framework for this device: callout 565 * stopped, workqueue destroyed and callout mutex destroyed. 566 */ 567 void 568 sme_events_destroy(struct sysmon_envsys *sme) 569 { 570 KASSERT(mutex_owned(&sme->sme_mtx)); 571 572 callout_stop(&sme->sme_callout); 573 workqueue_destroy(sme->sme_wq); 574 mutex_destroy(&sme->sme_callout_mtx); 575 callout_destroy(&sme->sme_callout); 576 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; 577 DPRINTF(("%s: events framework destroyed for '%s'\n", 578 __func__, sme->sme_name)); 579 } 580 581 /* 582 * sme_events_check: 583 * 584 * + Passes the events to the workqueue thread and stops 585 * the callout if the 'low-power' condition is triggered. 586 */ 587 void 588 sme_events_check(void *arg) 589 { 590 struct sysmon_envsys *sme = arg; 591 sme_event_t *see; 592 uint64_t timo; 593 594 KASSERT(sme != NULL); 595 596 mutex_enter(&sme->sme_callout_mtx); 597 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 598 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); 599 see->see_edata->flags |= ENVSYS_FNEED_REFRESH; 600 } 601 if (sme->sme_events_timeout) 602 timo = sme->sme_events_timeout * hz; 603 else 604 timo = SME_EVTIMO; 605 if (!sysmon_low_power) 606 callout_schedule(&sme->sme_callout, timo); 607 mutex_exit(&sme->sme_callout_mtx); 608 } 609 610 /* 611 * sme_events_worker: 612 * 613 * + workqueue thread that checks if there's a critical condition 614 * and sends an event if it was triggered. 615 */ 616 void 617 sme_events_worker(struct work *wk, void *arg) 618 { 619 const struct sme_description_table *sdt = NULL; 620 const struct sme_sensor_event *sse = sme_sensor_event; 621 sme_event_t *see = (void *)wk; 622 struct sysmon_envsys *sme = see->see_sme; 623 envsys_data_t *edata = see->see_edata; 624 int i, state = 0; 625 626 KASSERT(wk == &see->see_wk); 627 KASSERT(sme != NULL || edata != NULL); 628 629 mutex_enter(&sme->sme_mtx); 630 if ((see->see_flags & SEE_EVENT_WORKING) == 0) 631 see->see_flags |= SEE_EVENT_WORKING; 632 /* 633 * sme_events_check marks the sensors to make us refresh them here. 634 * Don't refresh if the driver uses its own method for refreshing. 635 */ 636 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 637 if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) { 638 /* refresh sensor in device */ 639 (*sme->sme_refresh)(sme, edata); 640 edata->flags &= ~ENVSYS_FNEED_REFRESH; 641 } 642 } 643 644 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d " 645 "value_cur=%d\n", __func__, sme->sme_name, edata->desc, 646 edata->sensor, see->see_type, edata->state, edata->units, 647 edata->value_cur)); 648 649 /* skip the event if current sensor is in invalid state */ 650 if (edata->state == ENVSYS_SINVALID) 651 goto out; 652 653 switch (see->see_type) { 654 /* 655 * For range limits, if the driver claims responsibility for 656 * limit/range checking, just user driver-supplied status. 657 * Else calculate our own status. Note that driver must 658 * relinquish responsibility for ALL limits if there is even 659 * one limit that it cannot handle! 660 */ 661 case PENVSYS_EVENT_LIMITS: 662 case PENVSYS_EVENT_CAPACITY: 663 #define __EXCEED_LIM(valid, lim, rel) \ 664 ((see->see_lims.sel_flags & (valid)) && \ 665 (edata->value_cur rel (see->see_lims.lim))) 666 667 if ((see->see_lims.sel_flags & PROP_DRIVER_LIMITS) == 0) { 668 if __EXCEED_LIM(PROP_CRITMIN | PROP_BATTCAP, 669 sel_critmin, <) 670 edata->state = ENVSYS_SCRITUNDER; 671 else if __EXCEED_LIM(PROP_WARNMIN | PROP_BATTWARN, 672 sel_warnmin, <) 673 edata->state = ENVSYS_SWARNUNDER; 674 else if __EXCEED_LIM(PROP_WARNMAX, sel_warnmax, >) 675 edata->state = ENVSYS_SWARNOVER; 676 else if __EXCEED_LIM(PROP_CRITMAX, sel_critmax, >) 677 edata->state = ENVSYS_SCRITOVER; 678 } 679 #undef __EXCEED_LIM 680 681 /* 682 * Send event if state has changed 683 */ 684 if (edata->state == see->see_evsent) 685 break; 686 687 for (i = 0; sse[i].state != -1; i++) 688 if (sse[i].state == edata->state) 689 break; 690 691 if (sse[i].state == -1) 692 break; 693 694 if (edata->state == ENVSYS_SVALID) 695 sysmon_penvsys_event(&see->see_pes, 696 PENVSYS_EVENT_NORMAL); 697 else 698 sysmon_penvsys_event(&see->see_pes, sse[i].event); 699 700 see->see_evsent = edata->state; 701 702 break; 703 704 /* 705 * Send PENVSYS_EVENT_CRITICAL event if: 706 * State has gone from non-CRITICAL to CRITICAL, 707 * State remains CRITICAL and value has changed, or 708 * State has returned from CRITICAL to non-CRITICAL 709 */ 710 case PENVSYS_EVENT_CRITICAL: 711 if (edata->state == ENVSYS_SVALID && 712 see->see_evsent != 0) { 713 sysmon_penvsys_event(&see->see_pes, 714 PENVSYS_EVENT_NORMAL); 715 see->see_evsent = 0; 716 } else if (edata->state == ENVSYS_SCRITICAL && 717 see->see_evsent != edata->value_cur) { 718 sysmon_penvsys_event(&see->see_pes, 719 PENVSYS_EVENT_CRITICAL); 720 see->see_evsent = edata->value_cur; 721 } 722 break; 723 724 /* 725 * if value_cur is not normal (battery) or online (drive), 726 * send the event... 727 */ 728 case PENVSYS_EVENT_STATE_CHANGED: 729 /* 730 * the state has not been changed, just ignore the event. 731 */ 732 if (edata->value_cur == see->see_evsent) 733 break; 734 735 switch (edata->units) { 736 case ENVSYS_DRIVE: 737 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 738 state = ENVSYS_DRIVE_ONLINE; 739 break; 740 case ENVSYS_BATTERY_CAPACITY: 741 sdt = sme_get_description_table( 742 SME_DESC_BATTERY_CAPACITY); 743 state = ENVSYS_BATTERY_CAPACITY_NORMAL; 744 break; 745 default: 746 panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED", 747 __func__); 748 } 749 750 for (i = 0; sdt[i].type != -1; i++) 751 if (sdt[i].type == edata->value_cur) 752 break; 753 754 if (sdt[i].type == -1) 755 break; 756 757 /* 758 * copy current state description. 759 */ 760 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc, 761 sizeof(see->see_pes.pes_statedesc)); 762 763 if (edata->value_cur == state) 764 /* 765 * state returned to normal condition 766 */ 767 sysmon_penvsys_event(&see->see_pes, 768 PENVSYS_EVENT_NORMAL); 769 else 770 /* 771 * state changed to abnormal condition 772 */ 773 sysmon_penvsys_event(&see->see_pes, see->see_type); 774 775 see->see_evsent = edata->value_cur; 776 777 /* 778 * There's no need to continue if it's a drive sensor. 779 */ 780 if (edata->units == ENVSYS_DRIVE) 781 break; 782 783 /* 784 * Check if the system is running in low power and send the 785 * event to powerd (if running) or shutdown the system 786 * otherwise. 787 */ 788 if (!sysmon_low_power && sme_event_check_low_power()) { 789 struct penvsys_state pes; 790 791 /* 792 * Stop the callout and send the 'low-power' event. 793 */ 794 sysmon_low_power = true; 795 callout_stop(&sme->sme_callout); 796 pes.pes_type = PENVSYS_TYPE_BATTERY; 797 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); 798 } 799 break; 800 default: 801 panic("%s: invalid event type %d", __func__, see->see_type); 802 } 803 804 out: 805 see->see_flags &= ~SEE_EVENT_WORKING; 806 cv_broadcast(&sme->sme_condvar); 807 mutex_exit(&sme->sme_mtx); 808 } 809 810 /* 811 * Returns true if the system is in low power state: an AC adapter 812 * is OFF and all batteries are in LOW/CRITICAL state. 813 */ 814 static bool 815 sme_event_check_low_power(void) 816 { 817 if (!sme_acadapter_check()) 818 return false; 819 820 return sme_battery_check(); 821 } 822 823 /* 824 * Called with the sysmon_envsys device mtx held through the 825 * workqueue thread. 826 */ 827 static bool 828 sme_acadapter_check(void) 829 { 830 struct sysmon_envsys *sme; 831 envsys_data_t *edata; 832 bool dev = false, sensor = false; 833 834 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 835 if (sme->sme_class == SME_CLASS_ACADAPTER) { 836 dev = true; 837 break; 838 } 839 } 840 841 /* 842 * No AC Adapter devices were found. 843 */ 844 if (!dev) 845 return false; 846 847 /* 848 * Check if there's an AC adapter device connected. 849 */ 850 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 851 if (edata->units == ENVSYS_INDICATOR) { 852 sensor = true; 853 /* refresh current sensor */ 854 (*sme->sme_refresh)(sme, edata); 855 if (edata->value_cur) 856 return false; 857 } 858 } 859 860 if (!sensor) 861 return false; 862 863 /* 864 * AC adapter found and not connected. 865 */ 866 return true; 867 } 868 869 /* 870 * Called with the sysmon_envsys device mtx held through the 871 * workqueue thread. 872 */ 873 static bool 874 sme_battery_check(void) 875 { 876 struct sysmon_envsys *sme; 877 envsys_data_t *edata; 878 int batteriesfound = 0; 879 bool present, batterycap, batterycharge; 880 881 /* 882 * Check for battery devices and its state. 883 */ 884 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 885 if (sme->sme_class != SME_CLASS_BATTERY) 886 continue; 887 888 present = true; 889 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 890 if (edata->units == ENVSYS_INDICATOR && 891 !edata->value_cur) { 892 present = false; 893 break; 894 } 895 } 896 if (!present) 897 continue; 898 /* 899 * We've found a battery device... 900 */ 901 batteriesfound++; 902 batterycap = batterycharge = false; 903 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 904 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 905 batterycap = true; 906 if (!sme_battery_critical(edata)) 907 return false; 908 } else if (edata->units == ENVSYS_BATTERY_CHARGE) { 909 batterycharge = true; 910 if (edata->value_cur) 911 return false; 912 } 913 } 914 if (!batterycap || !batterycharge) 915 return false; 916 } 917 918 if (!batteriesfound) 919 return false; 920 921 /* 922 * All batteries in low/critical capacity and discharging. 923 */ 924 return true; 925 } 926 927 static bool 928 sme_battery_critical(envsys_data_t *edata) 929 { 930 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL || 931 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW) 932 return true; 933 934 return false; 935 } 936