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