1 /* $NetBSD: sysmon_envsys.c,v 1.130 2015/04/13 16:33:25 riastradh 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 * Copyright (c) 2000 Zembu Labs, Inc. 30 * All rights reserved. 31 * 32 * Author: Jason R. Thorpe <thorpej@zembu.com> 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 2. Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in the 41 * documentation and/or other materials provided with the distribution. 42 * 3. All advertising materials mentioning features or use of this software 43 * must display the following acknowledgement: 44 * This product includes software developed by Zembu Labs, Inc. 45 * 4. Neither the name of Zembu Labs nor the names of its employees may 46 * be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS 50 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- 51 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- 52 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, 53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 54 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 58 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61 /* 62 * Environmental sensor framework for sysmon, exported to userland 63 * with proplib(3). 64 */ 65 66 #include <sys/cdefs.h> 67 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.130 2015/04/13 16:33:25 riastradh Exp $"); 68 69 #include <sys/param.h> 70 #include <sys/types.h> 71 #include <sys/conf.h> 72 #include <sys/errno.h> 73 #include <sys/fcntl.h> 74 #include <sys/kernel.h> 75 #include <sys/systm.h> 76 #include <sys/proc.h> 77 #include <sys/mutex.h> 78 #include <sys/kmem.h> 79 #include <sys/rndsource.h> 80 81 #include <dev/sysmon/sysmonvar.h> 82 #include <dev/sysmon/sysmon_envsysvar.h> 83 #include <dev/sysmon/sysmon_taskq.h> 84 85 kmutex_t sme_global_mtx; 86 87 prop_dictionary_t sme_propd; 88 89 struct sysmon_envsys_lh sysmon_envsys_list; 90 91 static uint32_t sysmon_envsys_next_sensor_index; 92 static struct sysmon_envsys *sysmon_envsys_find_40(u_int); 93 94 static void sysmon_envsys_destroy_plist(prop_array_t); 95 static void sme_remove_userprops(void); 96 static int sme_add_property_dictionary(struct sysmon_envsys *, prop_array_t, 97 prop_dictionary_t); 98 static sme_event_drv_t * sme_add_sensor_dictionary(struct sysmon_envsys *, 99 prop_array_t, prop_dictionary_t, envsys_data_t *); 100 static void sme_initial_refresh(void *); 101 static uint32_t sme_get_max_value(struct sysmon_envsys *, 102 bool (*)(const envsys_data_t*), bool); 103 104 /* 105 * sysmon_envsys_init: 106 * 107 * + Initialize global mutex, dictionary and the linked list. 108 */ 109 void 110 sysmon_envsys_init(void) 111 { 112 LIST_INIT(&sysmon_envsys_list); 113 mutex_init(&sme_global_mtx, MUTEX_DEFAULT, IPL_NONE); 114 sme_propd = prop_dictionary_create(); 115 } 116 117 /* 118 * sysmonopen_envsys: 119 * 120 * + Open the system monitor device. 121 */ 122 int 123 sysmonopen_envsys(dev_t dev, int flag, int mode, struct lwp *l) 124 { 125 return 0; 126 } 127 128 /* 129 * sysmonclose_envsys: 130 * 131 * + Close the system monitor device. 132 */ 133 int 134 sysmonclose_envsys(dev_t dev, int flag, int mode, struct lwp *l) 135 { 136 return 0; 137 } 138 139 /* 140 * sysmonioctl_envsys: 141 * 142 * + Perform a sysmon envsys control request. 143 */ 144 int 145 sysmonioctl_envsys(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 146 { 147 struct sysmon_envsys *sme = NULL; 148 int error = 0; 149 u_int oidx; 150 151 switch (cmd) { 152 /* 153 * To update the global dictionary with latest data from devices. 154 */ 155 case ENVSYS_GETDICTIONARY: 156 { 157 struct plistref *plist = (struct plistref *)data; 158 159 /* 160 * Update dictionaries on all sysmon envsys devices 161 * registered. 162 */ 163 mutex_enter(&sme_global_mtx); 164 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 165 sysmon_envsys_acquire(sme, false); 166 error = sme_update_dictionary(sme); 167 if (error) { 168 DPRINTF(("%s: sme_update_dictionary, " 169 "error=%d\n", __func__, error)); 170 sysmon_envsys_release(sme, false); 171 mutex_exit(&sme_global_mtx); 172 return error; 173 } 174 sysmon_envsys_release(sme, false); 175 } 176 mutex_exit(&sme_global_mtx); 177 /* 178 * Copy global dictionary to userland. 179 */ 180 error = prop_dictionary_copyout_ioctl(plist, cmd, sme_propd); 181 break; 182 } 183 /* 184 * To set properties on multiple devices. 185 */ 186 case ENVSYS_SETDICTIONARY: 187 { 188 const struct plistref *plist = (const struct plistref *)data; 189 prop_dictionary_t udict; 190 prop_object_iterator_t iter, iter2; 191 prop_object_t obj, obj2; 192 prop_array_t array_u, array_k; 193 const char *devname = NULL; 194 195 if ((flag & FWRITE) == 0) 196 return EPERM; 197 198 /* 199 * Get dictionary from userland. 200 */ 201 error = prop_dictionary_copyin_ioctl(plist, cmd, &udict); 202 if (error) { 203 DPRINTF(("%s: copyin_ioctl error=%d\n", 204 __func__, error)); 205 break; 206 } 207 208 iter = prop_dictionary_iterator(udict); 209 if (!iter) { 210 prop_object_release(udict); 211 return ENOMEM; 212 } 213 214 /* 215 * Iterate over the userland dictionary and process 216 * the list of devices. 217 */ 218 while ((obj = prop_object_iterator_next(iter))) { 219 array_u = prop_dictionary_get_keysym(udict, obj); 220 if (prop_object_type(array_u) != PROP_TYPE_ARRAY) { 221 prop_object_iterator_release(iter); 222 prop_object_release(udict); 223 return EINVAL; 224 } 225 226 devname = prop_dictionary_keysym_cstring_nocopy(obj); 227 DPRINTF(("%s: processing the '%s' array requests\n", 228 __func__, devname)); 229 230 /* 231 * find the correct sme device. 232 */ 233 sme = sysmon_envsys_find(devname); 234 if (!sme) { 235 DPRINTF(("%s: NULL sme\n", __func__)); 236 prop_object_iterator_release(iter); 237 prop_object_release(udict); 238 return EINVAL; 239 } 240 241 /* 242 * Find the correct array object with the string 243 * supplied by the userland dictionary. 244 */ 245 array_k = prop_dictionary_get(sme_propd, devname); 246 if (prop_object_type(array_k) != PROP_TYPE_ARRAY) { 247 DPRINTF(("%s: array device failed\n", 248 __func__)); 249 sysmon_envsys_release(sme, false); 250 prop_object_iterator_release(iter); 251 prop_object_release(udict); 252 return EINVAL; 253 } 254 255 iter2 = prop_array_iterator(array_u); 256 if (!iter2) { 257 sysmon_envsys_release(sme, false); 258 prop_object_iterator_release(iter); 259 prop_object_release(udict); 260 return ENOMEM; 261 } 262 263 /* 264 * Iterate over the array of dictionaries to 265 * process the list of sensors and properties. 266 */ 267 while ((obj2 = prop_object_iterator_next(iter2))) { 268 /* 269 * do the real work now. 270 */ 271 error = sme_userset_dictionary(sme, 272 obj2, 273 array_k); 274 if (error) { 275 sysmon_envsys_release(sme, false); 276 prop_object_iterator_release(iter2); 277 prop_object_iterator_release(iter); 278 prop_object_release(udict); 279 return error; 280 } 281 } 282 283 sysmon_envsys_release(sme, false); 284 prop_object_iterator_release(iter2); 285 } 286 287 prop_object_iterator_release(iter); 288 prop_object_release(udict); 289 break; 290 } 291 /* 292 * To remove all properties from all devices registered. 293 */ 294 case ENVSYS_REMOVEPROPS: 295 { 296 const struct plistref *plist = (const struct plistref *)data; 297 prop_dictionary_t udict; 298 prop_object_t obj; 299 300 if ((flag & FWRITE) == 0) 301 return EPERM; 302 303 error = prop_dictionary_copyin_ioctl(plist, cmd, &udict); 304 if (error) { 305 DPRINTF(("%s: copyin_ioctl error=%d\n", 306 __func__, error)); 307 break; 308 } 309 310 obj = prop_dictionary_get(udict, "envsys-remove-props"); 311 if (!obj || !prop_bool_true(obj)) { 312 DPRINTF(("%s: invalid 'envsys-remove-props'\n", 313 __func__)); 314 return EINVAL; 315 } 316 317 prop_object_release(udict); 318 sme_remove_userprops(); 319 320 break; 321 } 322 /* 323 * Compatibility ioctls with the old interface, only implemented 324 * ENVSYS_GTREDATA and ENVSYS_GTREINFO; enough to make old 325 * applications work. 326 */ 327 case ENVSYS_GTREDATA: 328 { 329 struct envsys_tre_data *tred = (void *)data; 330 envsys_data_t *edata = NULL; 331 bool found = false; 332 333 tred->validflags = 0; 334 335 sme = sysmon_envsys_find_40(tred->sensor); 336 if (!sme) 337 break; 338 339 oidx = tred->sensor; 340 tred->sensor = SME_SENSOR_IDX(sme, tred->sensor); 341 342 DPRINTFOBJ(("%s: sensor=%d oidx=%d dev=%s nsensors=%d\n", 343 __func__, tred->sensor, oidx, sme->sme_name, 344 sme->sme_nsensors)); 345 346 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 347 if (edata->sensor == tred->sensor) { 348 found = true; 349 break; 350 } 351 } 352 353 if (!found) { 354 sysmon_envsys_release(sme, false); 355 error = ENODEV; 356 break; 357 } 358 359 if (tred->sensor < sme->sme_nsensors) { 360 if ((sme->sme_flags & SME_POLL_ONLY) == 0) { 361 mutex_enter(&sme->sme_mtx); 362 sysmon_envsys_refresh_sensor(sme, edata); 363 mutex_exit(&sme->sme_mtx); 364 } 365 366 /* 367 * copy required values to the old interface. 368 */ 369 tred->sensor = edata->sensor; 370 tred->cur.data_us = edata->value_cur; 371 tred->cur.data_s = edata->value_cur; 372 tred->max.data_us = edata->value_max; 373 tred->max.data_s = edata->value_max; 374 tred->min.data_us = edata->value_min; 375 tred->min.data_s = edata->value_min; 376 tred->avg.data_us = 0; 377 tred->avg.data_s = 0; 378 if (edata->units == ENVSYS_BATTERY_CHARGE) 379 tred->units = ENVSYS_INDICATOR; 380 else 381 tred->units = edata->units; 382 383 tred->validflags |= ENVSYS_FVALID; 384 tred->validflags |= ENVSYS_FCURVALID; 385 386 if (edata->flags & ENVSYS_FPERCENT) { 387 tred->validflags |= ENVSYS_FMAXVALID; 388 tred->validflags |= ENVSYS_FFRACVALID; 389 } 390 391 if (edata->state == ENVSYS_SINVALID) { 392 tred->validflags &= ~ENVSYS_FCURVALID; 393 tred->cur.data_us = tred->cur.data_s = 0; 394 } 395 396 DPRINTFOBJ(("%s: sensor=%s tred->cur.data_s=%d\n", 397 __func__, edata->desc, tred->cur.data_s)); 398 DPRINTFOBJ(("%s: tred->validflags=%d tred->units=%d" 399 " tred->sensor=%d\n", __func__, tred->validflags, 400 tred->units, tred->sensor)); 401 } 402 tred->sensor = oidx; 403 sysmon_envsys_release(sme, false); 404 405 break; 406 } 407 case ENVSYS_GTREINFO: 408 { 409 struct envsys_basic_info *binfo = (void *)data; 410 envsys_data_t *edata = NULL; 411 bool found = false; 412 413 binfo->validflags = 0; 414 415 sme = sysmon_envsys_find_40(binfo->sensor); 416 if (!sme) 417 break; 418 419 oidx = binfo->sensor; 420 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 421 422 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 423 if (edata->sensor == binfo->sensor) { 424 found = true; 425 break; 426 } 427 } 428 429 if (!found) { 430 sysmon_envsys_release(sme, false); 431 error = ENODEV; 432 break; 433 } 434 435 binfo->validflags |= ENVSYS_FVALID; 436 437 if (binfo->sensor < sme->sme_nsensors) { 438 if (edata->units == ENVSYS_BATTERY_CHARGE) 439 binfo->units = ENVSYS_INDICATOR; 440 else 441 binfo->units = edata->units; 442 443 /* 444 * previously, the ACPI sensor names included the 445 * device name. Include that in compatibility code. 446 */ 447 if (strncmp(sme->sme_name, "acpi", 4) == 0) 448 (void)snprintf(binfo->desc, sizeof(binfo->desc), 449 "%s %s", sme->sme_name, edata->desc); 450 else 451 (void)strlcpy(binfo->desc, edata->desc, 452 sizeof(binfo->desc)); 453 } 454 455 DPRINTFOBJ(("%s: binfo->units=%d binfo->validflags=%d\n", 456 __func__, binfo->units, binfo->validflags)); 457 DPRINTFOBJ(("%s: binfo->desc=%s binfo->sensor=%d\n", 458 __func__, binfo->desc, binfo->sensor)); 459 460 binfo->sensor = oidx; 461 sysmon_envsys_release(sme, false); 462 463 break; 464 } 465 default: 466 error = ENOTTY; 467 break; 468 } 469 470 return error; 471 } 472 473 /* 474 * sysmon_envsys_create: 475 * 476 * + Allocates a new sysmon_envsys object and initializes the 477 * stuff for sensors and events. 478 */ 479 struct sysmon_envsys * 480 sysmon_envsys_create(void) 481 { 482 struct sysmon_envsys *sme; 483 484 sme = kmem_zalloc(sizeof(*sme), KM_SLEEP); 485 TAILQ_INIT(&sme->sme_sensors_list); 486 LIST_INIT(&sme->sme_events_list); 487 mutex_init(&sme->sme_mtx, MUTEX_DEFAULT, IPL_NONE); 488 mutex_init(&sme->sme_work_mtx, MUTEX_DEFAULT, IPL_NONE); 489 cv_init(&sme->sme_condvar, "sme_wait"); 490 491 return sme; 492 } 493 494 /* 495 * sysmon_envsys_destroy: 496 * 497 * + Removes all sensors from the tail queue, destroys the callout 498 * and frees the sysmon_envsys object. 499 */ 500 void 501 sysmon_envsys_destroy(struct sysmon_envsys *sme) 502 { 503 envsys_data_t *edata; 504 505 KASSERT(sme != NULL); 506 507 while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { 508 edata = TAILQ_FIRST(&sme->sme_sensors_list); 509 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 510 } 511 mutex_destroy(&sme->sme_mtx); 512 mutex_destroy(&sme->sme_work_mtx); 513 cv_destroy(&sme->sme_condvar); 514 kmem_free(sme, sizeof(*sme)); 515 } 516 517 /* 518 * sysmon_envsys_sensor_attach: 519 * 520 * + Attaches a sensor into a sysmon_envsys device checking that units 521 * is set to a valid type and description is unique and not empty. 522 */ 523 int 524 sysmon_envsys_sensor_attach(struct sysmon_envsys *sme, envsys_data_t *edata) 525 { 526 const struct sme_descr_entry *sdt_units; 527 envsys_data_t *oedata; 528 529 KASSERT(sme != NULL || edata != NULL); 530 531 /* 532 * Find the correct units for this sensor. 533 */ 534 sdt_units = sme_find_table_entry(SME_DESC_UNITS, edata->units); 535 if (sdt_units->type == -1) 536 return EINVAL; 537 538 /* 539 * Check that description is not empty or duplicate. 540 */ 541 if (strlen(edata->desc) == 0) 542 return EINVAL; 543 544 mutex_enter(&sme->sme_mtx); 545 sysmon_envsys_acquire(sme, true); 546 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 547 if (strcmp(oedata->desc, edata->desc) == 0) { 548 sysmon_envsys_release(sme, true); 549 mutex_exit(&sme->sme_mtx); 550 return EEXIST; 551 } 552 } 553 /* 554 * Ok, the sensor has been added into the device queue. 555 */ 556 TAILQ_INSERT_TAIL(&sme->sme_sensors_list, edata, sensors_head); 557 558 /* 559 * Give the sensor an index position. 560 */ 561 edata->sensor = sme->sme_nsensors; 562 sme->sme_nsensors++; 563 sysmon_envsys_release(sme, true); 564 mutex_exit(&sme->sme_mtx); 565 566 DPRINTF(("%s: attached #%d (%s), units=%d (%s)\n", 567 __func__, edata->sensor, edata->desc, 568 sdt_units->type, sdt_units->desc)); 569 570 return 0; 571 } 572 573 /* 574 * sysmon_envsys_sensor_detach: 575 * 576 * + Detachs a sensor from a sysmon_envsys device and decrements the 577 * sensors count on success. 578 */ 579 int 580 sysmon_envsys_sensor_detach(struct sysmon_envsys *sme, envsys_data_t *edata) 581 { 582 envsys_data_t *oedata; 583 bool found = false; 584 bool destroy = false; 585 586 KASSERT(sme != NULL || edata != NULL); 587 588 /* 589 * Check the sensor is already on the list. 590 */ 591 mutex_enter(&sme->sme_mtx); 592 sysmon_envsys_acquire(sme, true); 593 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 594 if (oedata->sensor == edata->sensor) { 595 found = true; 596 break; 597 } 598 } 599 600 if (!found) { 601 sysmon_envsys_release(sme, true); 602 mutex_exit(&sme->sme_mtx); 603 return EINVAL; 604 } 605 606 /* 607 * remove it, unhook from rnd(4), and decrement the sensors count. 608 */ 609 sme_event_unregister_sensor(sme, edata); 610 if (LIST_EMPTY(&sme->sme_events_list)) { 611 sme_events_halt_callout(sme); 612 destroy = true; 613 } 614 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 615 sme->sme_nsensors--; 616 sysmon_envsys_release(sme, true); 617 mutex_exit(&sme->sme_mtx); 618 619 if (destroy) 620 sme_events_destroy(sme); 621 622 return 0; 623 } 624 625 626 /* 627 * sysmon_envsys_register: 628 * 629 * + Register a sysmon envsys device. 630 * + Create array of dictionaries for a device. 631 */ 632 int 633 sysmon_envsys_register(struct sysmon_envsys *sme) 634 { 635 struct sme_evdrv { 636 SLIST_ENTRY(sme_evdrv) evdrv_head; 637 sme_event_drv_t *evdrv; 638 }; 639 SLIST_HEAD(, sme_evdrv) sme_evdrv_list; 640 struct sme_evdrv *evdv = NULL; 641 struct sysmon_envsys *lsme; 642 prop_array_t array = NULL; 643 prop_dictionary_t dict, dict2; 644 envsys_data_t *edata = NULL; 645 sme_event_drv_t *this_evdrv; 646 int nevent; 647 int error = 0; 648 char rnd_name[sizeof(edata->rnd_src.name)]; 649 650 KASSERT(sme != NULL); 651 KASSERT(sme->sme_name != NULL); 652 653 /* 654 * Check if requested sysmon_envsys device is valid 655 * and does not exist already in the list. 656 */ 657 mutex_enter(&sme_global_mtx); 658 LIST_FOREACH(lsme, &sysmon_envsys_list, sme_list) { 659 if (strcmp(lsme->sme_name, sme->sme_name) == 0) { 660 mutex_exit(&sme_global_mtx); 661 return EEXIST; 662 } 663 } 664 mutex_exit(&sme_global_mtx); 665 666 /* 667 * sanity check: if SME_DISABLE_REFRESH is not set, 668 * the sme_refresh function callback must be non NULL. 669 */ 670 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 671 if (!sme->sme_refresh) 672 return EINVAL; 673 674 /* 675 * If the list of sensors is empty, there's no point to continue... 676 */ 677 if (TAILQ_EMPTY(&sme->sme_sensors_list)) { 678 DPRINTF(("%s: sensors list empty for %s\n", __func__, 679 sme->sme_name)); 680 return ENOTSUP; 681 } 682 683 /* 684 * Initialize the singly linked list for driver events. 685 */ 686 SLIST_INIT(&sme_evdrv_list); 687 688 array = prop_array_create(); 689 if (!array) 690 return ENOMEM; 691 692 /* 693 * Iterate over all sensors and create a dictionary per sensor. 694 * We must respect the order in which the sensors were added. 695 */ 696 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 697 dict = prop_dictionary_create(); 698 if (!dict) { 699 error = ENOMEM; 700 goto out2; 701 } 702 703 /* 704 * Create all objects in sensor's dictionary. 705 */ 706 this_evdrv = sme_add_sensor_dictionary(sme, array, 707 dict, edata); 708 if (this_evdrv) { 709 evdv = kmem_zalloc(sizeof(*evdv), KM_SLEEP); 710 evdv->evdrv = this_evdrv; 711 SLIST_INSERT_HEAD(&sme_evdrv_list, evdv, evdrv_head); 712 } 713 } 714 715 /* 716 * If the array does not contain any object (sensor), there's 717 * no need to attach the driver. 718 */ 719 if (prop_array_count(array) == 0) { 720 error = EINVAL; 721 DPRINTF(("%s: empty array for '%s'\n", __func__, 722 sme->sme_name)); 723 goto out; 724 } 725 726 /* 727 * Add the dictionary for the global properties of this device. 728 */ 729 dict2 = prop_dictionary_create(); 730 if (!dict2) { 731 error = ENOMEM; 732 goto out; 733 } 734 735 error = sme_add_property_dictionary(sme, array, dict2); 736 if (error) { 737 prop_object_release(dict2); 738 goto out; 739 } 740 741 /* 742 * Add the array into the global dictionary for the driver. 743 * 744 * <dict> 745 * <key>foo0</key> 746 * <array> 747 * ... 748 */ 749 mutex_enter(&sme_global_mtx); 750 if (!prop_dictionary_set(sme_propd, sme->sme_name, array)) { 751 error = EINVAL; 752 DPRINTF(("%s: prop_dictionary_set for '%s'\n", __func__, 753 sme->sme_name)); 754 goto out; 755 } 756 757 /* 758 * Add the device into the list. 759 */ 760 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); 761 sme->sme_fsensor = sysmon_envsys_next_sensor_index; 762 sysmon_envsys_next_sensor_index += sme->sme_nsensors; 763 mutex_exit(&sme_global_mtx); 764 765 out: 766 /* 767 * No errors? Make an initial data refresh if was requested, 768 * then register the events that were set in the driver. Do 769 * the refresh first in case it is needed to establish the 770 * limits or max_value needed by some events. 771 */ 772 if (error == 0) { 773 nevent = 0; 774 sysmon_task_queue_init(); 775 776 if (sme->sme_flags & SME_INIT_REFRESH) { 777 sysmon_task_queue_sched(0, sme_initial_refresh, sme); 778 DPRINTF(("%s: scheduled initial refresh for '%s'\n", 779 __func__, sme->sme_name)); 780 } 781 SLIST_FOREACH(evdv, &sme_evdrv_list, evdrv_head) { 782 sysmon_task_queue_sched(0, 783 sme_event_drvadd, evdv->evdrv); 784 nevent++; 785 } 786 /* 787 * Hook the sensor into rnd(4) entropy pool if requested 788 */ 789 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 790 if (edata->flags & ENVSYS_FHAS_ENTROPY) { 791 uint32_t rnd_type, rnd_flag = 0; 792 size_t n; 793 int tail = 1; 794 795 snprintf(rnd_name, sizeof(rnd_name), "%s-%s", 796 sme->sme_name, edata->desc); 797 n = strlen(rnd_name); 798 /* 799 * 1) Remove trailing white space(s). 800 * 2) If space exist, replace it with '-' 801 */ 802 while (--n) { 803 if (rnd_name[n] == ' ') { 804 if (tail != 0) 805 rnd_name[n] = '\0'; 806 else 807 rnd_name[n] = '-'; 808 } else 809 tail = 0; 810 } 811 rnd_flag |= RND_FLAG_COLLECT_TIME; 812 rnd_flag |= RND_FLAG_ESTIMATE_TIME; 813 814 switch (edata->units) { 815 case ENVSYS_STEMP: 816 case ENVSYS_SFANRPM: 817 case ENVSYS_INTEGER: 818 rnd_type = RND_TYPE_ENV; 819 rnd_flag |= RND_FLAG_COLLECT_VALUE; 820 rnd_flag |= RND_FLAG_ESTIMATE_VALUE; 821 break; 822 case ENVSYS_SVOLTS_AC: 823 case ENVSYS_SVOLTS_DC: 824 case ENVSYS_SOHMS: 825 case ENVSYS_SWATTS: 826 case ENVSYS_SAMPS: 827 case ENVSYS_SWATTHOUR: 828 case ENVSYS_SAMPHOUR: 829 rnd_type = RND_TYPE_POWER; 830 rnd_flag |= RND_FLAG_COLLECT_VALUE; 831 rnd_flag |= RND_FLAG_ESTIMATE_VALUE; 832 break; 833 default: 834 rnd_type = RND_TYPE_UNKNOWN; 835 break; 836 } 837 rnd_attach_source(&edata->rnd_src, rnd_name, 838 rnd_type, rnd_flag); 839 } 840 } 841 DPRINTF(("%s: driver '%s' registered (nsens=%d nevent=%d)\n", 842 __func__, sme->sme_name, sme->sme_nsensors, nevent)); 843 } 844 845 out2: 846 while (!SLIST_EMPTY(&sme_evdrv_list)) { 847 evdv = SLIST_FIRST(&sme_evdrv_list); 848 SLIST_REMOVE_HEAD(&sme_evdrv_list, evdrv_head); 849 kmem_free(evdv, sizeof(*evdv)); 850 } 851 if (!error) 852 return 0; 853 854 /* 855 * Ugh... something wasn't right; unregister all events and sensors 856 * previously assigned and destroy the array with all its objects. 857 */ 858 DPRINTF(("%s: failed to register '%s' (%d)\n", __func__, 859 sme->sme_name, error)); 860 861 sme_event_unregister_all(sme); 862 while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { 863 edata = TAILQ_FIRST(&sme->sme_sensors_list); 864 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 865 } 866 sysmon_envsys_destroy_plist(array); 867 return error; 868 } 869 870 /* 871 * sysmon_envsys_destroy_plist: 872 * 873 * + Remove all objects from the array of dictionaries that is 874 * created in a sysmon envsys device. 875 */ 876 static void 877 sysmon_envsys_destroy_plist(prop_array_t array) 878 { 879 prop_object_iterator_t iter, iter2; 880 prop_dictionary_t dict; 881 prop_object_t obj; 882 883 KASSERT(array != NULL); 884 KASSERT(prop_object_type(array) == PROP_TYPE_ARRAY); 885 886 DPRINTFOBJ(("%s: objects in array=%d\n", __func__, 887 prop_array_count(array))); 888 889 iter = prop_array_iterator(array); 890 if (!iter) 891 return; 892 893 while ((dict = prop_object_iterator_next(iter))) { 894 KASSERT(prop_object_type(dict) == PROP_TYPE_DICTIONARY); 895 iter2 = prop_dictionary_iterator(dict); 896 if (!iter2) 897 goto out; 898 DPRINTFOBJ(("%s: iterating over dictionary\n", __func__)); 899 while ((obj = prop_object_iterator_next(iter2)) != NULL) { 900 DPRINTFOBJ(("%s: obj=%s\n", __func__, 901 prop_dictionary_keysym_cstring_nocopy(obj))); 902 prop_dictionary_remove(dict, 903 prop_dictionary_keysym_cstring_nocopy(obj)); 904 prop_object_iterator_reset(iter2); 905 } 906 prop_object_iterator_release(iter2); 907 DPRINTFOBJ(("%s: objects in dictionary:%d\n", 908 __func__, prop_dictionary_count(dict))); 909 prop_object_release(dict); 910 } 911 912 out: 913 prop_object_iterator_release(iter); 914 prop_object_release(array); 915 } 916 917 /* 918 * sysmon_envsys_unregister: 919 * 920 * + Unregister a sysmon envsys device. 921 */ 922 void 923 sysmon_envsys_unregister(struct sysmon_envsys *sme) 924 { 925 prop_array_t array; 926 struct sysmon_envsys *osme; 927 928 KASSERT(sme != NULL); 929 930 /* 931 * Decrement global sensors counter and the first_sensor index 932 * for remaining devices in the list (only used for compatibility 933 * with previous API), and remove the device from the list. 934 */ 935 mutex_enter(&sme_global_mtx); 936 sysmon_envsys_next_sensor_index -= sme->sme_nsensors; 937 LIST_FOREACH(osme, &sysmon_envsys_list, sme_list) { 938 if (osme->sme_fsensor >= sme->sme_fsensor) 939 osme->sme_fsensor -= sme->sme_nsensors; 940 } 941 LIST_REMOVE(sme, sme_list); 942 mutex_exit(&sme_global_mtx); 943 944 /* 945 * Unregister all events associated with device. 946 */ 947 sme_event_unregister_all(sme); 948 949 /* 950 * Remove the device (and all its objects) from the global dictionary. 951 */ 952 array = prop_dictionary_get(sme_propd, sme->sme_name); 953 if (array && prop_object_type(array) == PROP_TYPE_ARRAY) { 954 mutex_enter(&sme_global_mtx); 955 prop_dictionary_remove(sme_propd, sme->sme_name); 956 mutex_exit(&sme_global_mtx); 957 sysmon_envsys_destroy_plist(array); 958 } 959 /* 960 * And finally destroy the sysmon_envsys object. 961 */ 962 sysmon_envsys_destroy(sme); 963 } 964 965 /* 966 * sysmon_envsys_find: 967 * 968 * + Find a sysmon envsys device and mark it as busy 969 * once it's available. 970 */ 971 struct sysmon_envsys * 972 sysmon_envsys_find(const char *name) 973 { 974 struct sysmon_envsys *sme; 975 976 mutex_enter(&sme_global_mtx); 977 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 978 if (strcmp(sme->sme_name, name) == 0) { 979 sysmon_envsys_acquire(sme, false); 980 break; 981 } 982 } 983 mutex_exit(&sme_global_mtx); 984 985 return sme; 986 } 987 988 /* 989 * Compatibility function with the old API. 990 */ 991 struct sysmon_envsys * 992 sysmon_envsys_find_40(u_int idx) 993 { 994 struct sysmon_envsys *sme; 995 996 mutex_enter(&sme_global_mtx); 997 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 998 if (idx >= sme->sme_fsensor && 999 idx < (sme->sme_fsensor + sme->sme_nsensors)) { 1000 sysmon_envsys_acquire(sme, false); 1001 break; 1002 } 1003 } 1004 mutex_exit(&sme_global_mtx); 1005 1006 return sme; 1007 } 1008 1009 /* 1010 * sysmon_envsys_acquire: 1011 * 1012 * + Wait until a sysmon envsys device is available and mark 1013 * it as busy. 1014 */ 1015 void 1016 sysmon_envsys_acquire(struct sysmon_envsys *sme, bool locked) 1017 { 1018 KASSERT(sme != NULL); 1019 1020 if (locked) { 1021 while (sme->sme_flags & SME_FLAG_BUSY) 1022 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 1023 sme->sme_flags |= SME_FLAG_BUSY; 1024 } else { 1025 mutex_enter(&sme->sme_mtx); 1026 while (sme->sme_flags & SME_FLAG_BUSY) 1027 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 1028 sme->sme_flags |= SME_FLAG_BUSY; 1029 mutex_exit(&sme->sme_mtx); 1030 } 1031 } 1032 1033 /* 1034 * sysmon_envsys_release: 1035 * 1036 * + Unmark a sysmon envsys device as busy, and notify 1037 * waiters. 1038 */ 1039 void 1040 sysmon_envsys_release(struct sysmon_envsys *sme, bool locked) 1041 { 1042 KASSERT(sme != NULL); 1043 1044 if (locked) { 1045 sme->sme_flags &= ~SME_FLAG_BUSY; 1046 cv_broadcast(&sme->sme_condvar); 1047 } else { 1048 mutex_enter(&sme->sme_mtx); 1049 sme->sme_flags &= ~SME_FLAG_BUSY; 1050 cv_broadcast(&sme->sme_condvar); 1051 mutex_exit(&sme->sme_mtx); 1052 } 1053 } 1054 1055 /* 1056 * sme_initial_refresh: 1057 * 1058 * + Do an initial refresh of the sensors in a device just after 1059 * interrupts are enabled in the autoconf(9) process. 1060 * 1061 */ 1062 static void 1063 sme_initial_refresh(void *arg) 1064 { 1065 struct sysmon_envsys *sme = arg; 1066 envsys_data_t *edata; 1067 1068 mutex_enter(&sme->sme_mtx); 1069 sysmon_envsys_acquire(sme, true); 1070 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) 1071 sysmon_envsys_refresh_sensor(sme, edata); 1072 sysmon_envsys_release(sme, true); 1073 mutex_exit(&sme->sme_mtx); 1074 } 1075 1076 /* 1077 * sme_sensor_dictionary_get: 1078 * 1079 * + Returns a dictionary of a device specified by its index 1080 * position. 1081 */ 1082 prop_dictionary_t 1083 sme_sensor_dictionary_get(prop_array_t array, const char *index) 1084 { 1085 prop_object_iterator_t iter; 1086 prop_dictionary_t dict; 1087 prop_object_t obj; 1088 1089 KASSERT(array != NULL || index != NULL); 1090 1091 iter = prop_array_iterator(array); 1092 if (!iter) 1093 return NULL; 1094 1095 while ((dict = prop_object_iterator_next(iter))) { 1096 obj = prop_dictionary_get(dict, "index"); 1097 if (prop_string_equals_cstring(obj, index)) 1098 break; 1099 } 1100 1101 prop_object_iterator_release(iter); 1102 return dict; 1103 } 1104 1105 /* 1106 * sme_remove_userprops: 1107 * 1108 * + Remove all properties from all devices that were set by 1109 * the ENVSYS_SETDICTIONARY ioctl. 1110 */ 1111 static void 1112 sme_remove_userprops(void) 1113 { 1114 struct sysmon_envsys *sme; 1115 prop_array_t array; 1116 prop_dictionary_t sdict; 1117 envsys_data_t *edata = NULL; 1118 char tmp[ENVSYS_DESCLEN]; 1119 char rnd_name[sizeof(edata->rnd_src.name)]; 1120 sysmon_envsys_lim_t lims; 1121 const struct sme_descr_entry *sdt_units; 1122 uint32_t props; 1123 int ptype; 1124 1125 mutex_enter(&sme_global_mtx); 1126 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1127 sysmon_envsys_acquire(sme, false); 1128 array = prop_dictionary_get(sme_propd, sme->sme_name); 1129 1130 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1131 (void)snprintf(tmp, sizeof(tmp), "sensor%d", 1132 edata->sensor); 1133 sdict = sme_sensor_dictionary_get(array, tmp); 1134 KASSERT(sdict != NULL); 1135 1136 ptype = 0; 1137 if (edata->upropset & PROP_BATTCAP) { 1138 prop_dictionary_remove(sdict, 1139 "critical-capacity"); 1140 ptype = PENVSYS_EVENT_CAPACITY; 1141 } 1142 1143 if (edata->upropset & PROP_BATTWARN) { 1144 prop_dictionary_remove(sdict, 1145 "warning-capacity"); 1146 ptype = PENVSYS_EVENT_CAPACITY; 1147 } 1148 1149 if (edata->upropset & PROP_BATTHIGH) { 1150 prop_dictionary_remove(sdict, 1151 "high-capacity"); 1152 ptype = PENVSYS_EVENT_CAPACITY; 1153 } 1154 1155 if (edata->upropset & PROP_BATTMAX) { 1156 prop_dictionary_remove(sdict, 1157 "maximum-capacity"); 1158 ptype = PENVSYS_EVENT_CAPACITY; 1159 } 1160 if (edata->upropset & PROP_WARNMAX) { 1161 prop_dictionary_remove(sdict, "warning-max"); 1162 ptype = PENVSYS_EVENT_LIMITS; 1163 } 1164 1165 if (edata->upropset & PROP_WARNMIN) { 1166 prop_dictionary_remove(sdict, "warning-min"); 1167 ptype = PENVSYS_EVENT_LIMITS; 1168 } 1169 1170 if (edata->upropset & PROP_CRITMAX) { 1171 prop_dictionary_remove(sdict, "critical-max"); 1172 ptype = PENVSYS_EVENT_LIMITS; 1173 } 1174 1175 if (edata->upropset & PROP_CRITMIN) { 1176 prop_dictionary_remove(sdict, "critical-min"); 1177 ptype = PENVSYS_EVENT_LIMITS; 1178 } 1179 if (edata->upropset & PROP_RFACT) { 1180 (void)sme_sensor_upint32(sdict, "rfact", 0); 1181 edata->rfact = 0; 1182 } 1183 1184 if (edata->upropset & PROP_DESC) 1185 (void)sme_sensor_upstring(sdict, 1186 "description", edata->desc); 1187 1188 if (ptype == 0) 1189 continue; 1190 1191 /* 1192 * If there were any limit values removed, we 1193 * need to revert to initial limits. 1194 * 1195 * First, tell the driver that we need it to 1196 * restore any h/w limits which may have been 1197 * changed to stored, boot-time values. 1198 */ 1199 if (sme->sme_set_limits) { 1200 DPRINTF(("%s: reset limits for %s %s\n", 1201 __func__, sme->sme_name, edata->desc)); 1202 (*sme->sme_set_limits)(sme, edata, NULL, NULL); 1203 } 1204 1205 /* 1206 * Next, we need to retrieve those initial limits. 1207 */ 1208 props = 0; 1209 edata->upropset &= ~PROP_LIMITS; 1210 if (sme->sme_get_limits) { 1211 DPRINTF(("%s: retrieve limits for %s %s\n", 1212 __func__, sme->sme_name, edata->desc)); 1213 lims = edata->limits; 1214 (*sme->sme_get_limits)(sme, edata, &lims, 1215 &props); 1216 } 1217 1218 /* 1219 * Finally, remove any old limits event, then 1220 * install a new event (which will update the 1221 * dictionary) 1222 */ 1223 sme_event_unregister(sme, edata->desc, 1224 PENVSYS_EVENT_LIMITS); 1225 1226 /* 1227 * Find the correct units for this sensor. 1228 */ 1229 sdt_units = sme_find_table_entry(SME_DESC_UNITS, 1230 edata->units); 1231 1232 if (props & PROP_LIMITS) { 1233 DPRINTF(("%s: install limits for %s %s\n", 1234 __func__, sme->sme_name, edata->desc)); 1235 1236 sme_event_register(sdict, edata, sme, 1237 &lims, props, PENVSYS_EVENT_LIMITS, 1238 sdt_units->crittype); 1239 } 1240 if (edata->flags & ENVSYS_FHAS_ENTROPY) { 1241 sme_event_register(sdict, edata, sme, 1242 &lims, props, PENVSYS_EVENT_NULL, 1243 sdt_units->crittype); 1244 snprintf(rnd_name, sizeof(rnd_name), "%s-%s", 1245 sme->sme_name, edata->desc); 1246 rnd_attach_source(&edata->rnd_src, rnd_name, 1247 RND_TYPE_ENV, RND_FLAG_COLLECT_VALUE| 1248 RND_FLAG_COLLECT_TIME| 1249 RND_FLAG_ESTIMATE_VALUE| 1250 RND_FLAG_ESTIMATE_TIME); 1251 } 1252 } 1253 1254 /* 1255 * Restore default timeout value. 1256 */ 1257 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1258 sme_schedule_callout(sme); 1259 sysmon_envsys_release(sme, false); 1260 } 1261 mutex_exit(&sme_global_mtx); 1262 } 1263 1264 /* 1265 * sme_add_property_dictionary: 1266 * 1267 * + Add global properties into a device. 1268 */ 1269 static int 1270 sme_add_property_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1271 prop_dictionary_t dict) 1272 { 1273 prop_dictionary_t pdict; 1274 const char *class; 1275 int error = 0; 1276 1277 pdict = prop_dictionary_create(); 1278 if (!pdict) 1279 return EINVAL; 1280 1281 /* 1282 * Add the 'refresh-timeout' and 'dev-class' objects into the 1283 * 'device-properties' dictionary. 1284 * 1285 * ... 1286 * <dict> 1287 * <key>device-properties</key> 1288 * <dict> 1289 * <key>refresh-timeout</key> 1290 * <integer>120</integer< 1291 * <key>device-class</key> 1292 * <string>class_name</string> 1293 * </dict> 1294 * </dict> 1295 * ... 1296 * 1297 */ 1298 if (sme->sme_events_timeout == 0) { 1299 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1300 sme_schedule_callout(sme); 1301 } 1302 1303 if (!prop_dictionary_set_uint64(pdict, "refresh-timeout", 1304 sme->sme_events_timeout)) { 1305 error = EINVAL; 1306 goto out; 1307 } 1308 if (sme->sme_class == SME_CLASS_BATTERY) 1309 class = "battery"; 1310 else if (sme->sme_class == SME_CLASS_ACADAPTER) 1311 class = "ac-adapter"; 1312 else 1313 class = "other"; 1314 if (!prop_dictionary_set_cstring_nocopy(pdict, "device-class", class)) { 1315 error = EINVAL; 1316 goto out; 1317 } 1318 1319 if (!prop_dictionary_set(dict, "device-properties", pdict)) { 1320 error = EINVAL; 1321 goto out; 1322 } 1323 1324 /* 1325 * Add the device dictionary into the sysmon envsys array. 1326 */ 1327 if (!prop_array_add(array, dict)) 1328 error = EINVAL; 1329 1330 out: 1331 prop_object_release(pdict); 1332 return error; 1333 } 1334 1335 /* 1336 * sme_add_sensor_dictionary: 1337 * 1338 * + Adds the sensor objects into the dictionary and returns a pointer 1339 * to a sme_event_drv_t object if a monitoring flag was set 1340 * (or NULL otherwise). 1341 */ 1342 static sme_event_drv_t * 1343 sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1344 prop_dictionary_t dict, envsys_data_t *edata) 1345 { 1346 const struct sme_descr_entry *sdt; 1347 int error; 1348 sme_event_drv_t *sme_evdrv_t = NULL; 1349 char indexstr[ENVSYS_DESCLEN]; 1350 bool mon_supported, allow_rfact; 1351 1352 /* 1353 * Add the index sensor string. 1354 * 1355 * ... 1356 * <key>index</eyr 1357 * <string>sensor0</string> 1358 * ... 1359 */ 1360 (void)snprintf(indexstr, sizeof(indexstr), "sensor%d", edata->sensor); 1361 if (sme_sensor_upstring(dict, "index", indexstr)) 1362 goto bad; 1363 1364 /* 1365 * ... 1366 * <key>description</key> 1367 * <string>blah blah</string> 1368 * ... 1369 */ 1370 if (sme_sensor_upstring(dict, "description", edata->desc)) 1371 goto bad; 1372 1373 /* 1374 * Add the monitoring boolean object: 1375 * 1376 * ... 1377 * <key>monitoring-supported</key> 1378 * <true/> 1379 * ... 1380 * 1381 * always false on Battery {capacity,charge}, Drive and Indicator types. 1382 * They cannot be monitored. 1383 * 1384 */ 1385 if ((edata->flags & ENVSYS_FMONNOTSUPP) || 1386 (edata->units == ENVSYS_INDICATOR) || 1387 (edata->units == ENVSYS_DRIVE) || 1388 (edata->units == ENVSYS_BATTERY_CAPACITY) || 1389 (edata->units == ENVSYS_BATTERY_CHARGE)) 1390 mon_supported = false; 1391 else 1392 mon_supported = true; 1393 if (sme_sensor_upbool(dict, "monitoring-supported", mon_supported)) 1394 goto out; 1395 1396 /* 1397 * Add the allow-rfact boolean object, true if 1398 * ENVSYS_FCHANGERFACT is set, false otherwise. 1399 * 1400 * ... 1401 * <key>allow-rfact</key> 1402 * <true/> 1403 * ... 1404 */ 1405 if (edata->units == ENVSYS_SVOLTS_DC || 1406 edata->units == ENVSYS_SVOLTS_AC) { 1407 if (edata->flags & ENVSYS_FCHANGERFACT) 1408 allow_rfact = true; 1409 else 1410 allow_rfact = false; 1411 if (sme_sensor_upbool(dict, "allow-rfact", allow_rfact)) 1412 goto out; 1413 } 1414 1415 error = sme_update_sensor_dictionary(dict, edata, 1416 (edata->state == ENVSYS_SVALID)); 1417 if (error < 0) 1418 goto bad; 1419 else if (error) 1420 goto out; 1421 1422 /* 1423 * ... 1424 * </dict> 1425 * 1426 * Add the dictionary into the array. 1427 * 1428 */ 1429 if (!prop_array_add(array, dict)) { 1430 DPRINTF(("%s: prop_array_add\n", __func__)); 1431 goto bad; 1432 } 1433 1434 /* 1435 * Register new event(s) if any monitoring flag was set or if 1436 * the sensor provides entropy for rnd(4). 1437 */ 1438 if (edata->flags & (ENVSYS_FMONANY | ENVSYS_FHAS_ENTROPY)) { 1439 sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP); 1440 sme_evdrv_t->sed_sdict = dict; 1441 sme_evdrv_t->sed_edata = edata; 1442 sme_evdrv_t->sed_sme = sme; 1443 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1444 sme_evdrv_t->sed_powertype = sdt->crittype; 1445 } 1446 1447 out: 1448 return sme_evdrv_t; 1449 1450 bad: 1451 prop_object_release(dict); 1452 return NULL; 1453 } 1454 1455 /* 1456 * Find the maximum of all currently reported values. 1457 * The provided callback decides whether a sensor is part of the 1458 * maximum calculation (by returning true) or ignored (callback 1459 * returns false). Example usage: callback selects temperature 1460 * sensors in a given thermal zone, the function calculates the 1461 * maximum currently reported temperature in this zone. 1462 * If the parameter "refresh" is true, new values will be aquired 1463 * from the hardware, if not, the last reported value will be used. 1464 */ 1465 uint32_t 1466 sysmon_envsys_get_max_value(bool (*predicate)(const envsys_data_t*), 1467 bool refresh) 1468 { 1469 struct sysmon_envsys *sme; 1470 uint32_t maxv, v; 1471 1472 maxv = 0; 1473 mutex_enter(&sme_global_mtx); 1474 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1475 sysmon_envsys_acquire(sme, false); 1476 v = sme_get_max_value(sme, predicate, refresh); 1477 sysmon_envsys_release(sme, false); 1478 if (v > maxv) 1479 maxv = v; 1480 } 1481 mutex_exit(&sme_global_mtx); 1482 return maxv; 1483 } 1484 1485 static uint32_t 1486 sme_get_max_value(struct sysmon_envsys *sme, 1487 bool (*predicate)(const envsys_data_t*), 1488 bool refresh) 1489 { 1490 envsys_data_t *edata; 1491 uint32_t maxv, v; 1492 1493 /* 1494 * Iterate over all sensors that match the predicate 1495 */ 1496 maxv = 0; 1497 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1498 if (!(*predicate)(edata)) 1499 continue; 1500 1501 /* 1502 * refresh sensor data 1503 */ 1504 mutex_enter(&sme->sme_mtx); 1505 sysmon_envsys_refresh_sensor(sme, edata); 1506 mutex_exit(&sme->sme_mtx); 1507 1508 v = edata->value_cur; 1509 if (v > maxv) 1510 maxv = v; 1511 1512 } 1513 1514 return maxv; 1515 } 1516 1517 /* 1518 * sme_update_dictionary: 1519 * 1520 * + Update per-sensor dictionaries with new values if there were 1521 * changes, otherwise the object in dictionary is untouched. 1522 */ 1523 int 1524 sme_update_dictionary(struct sysmon_envsys *sme) 1525 { 1526 envsys_data_t *edata; 1527 prop_object_t array, dict, obj, obj2; 1528 int error = 0; 1529 1530 /* 1531 * Retrieve the array of dictionaries in device. 1532 */ 1533 array = prop_dictionary_get(sme_propd, sme->sme_name); 1534 if (prop_object_type(array) != PROP_TYPE_ARRAY) { 1535 DPRINTF(("%s: not an array (%s)\n", __func__, sme->sme_name)); 1536 return EINVAL; 1537 } 1538 1539 /* 1540 * Get the last dictionary on the array, this contains the 1541 * 'device-properties' sub-dictionary. 1542 */ 1543 obj = prop_array_get(array, prop_array_count(array) - 1); 1544 if (!obj || prop_object_type(obj) != PROP_TYPE_DICTIONARY) { 1545 DPRINTF(("%s: not a device-properties dictionary\n", __func__)); 1546 return EINVAL; 1547 } 1548 1549 obj2 = prop_dictionary_get(obj, "device-properties"); 1550 if (!obj2) 1551 return EINVAL; 1552 1553 /* 1554 * Update the 'refresh-timeout' property. 1555 */ 1556 if (!prop_dictionary_set_uint64(obj2, "refresh-timeout", 1557 sme->sme_events_timeout)) 1558 return EINVAL; 1559 1560 /* 1561 * - iterate over all sensors. 1562 * - fetch new data. 1563 * - check if data in dictionary is different than new data. 1564 * - update dictionary if there were changes. 1565 */ 1566 DPRINTF(("%s: updating '%s' with nsensors=%d\n", __func__, 1567 sme->sme_name, sme->sme_nsensors)); 1568 1569 /* 1570 * Don't bother with locking when traversing the queue, 1571 * the device is already marked as busy; if a sensor 1572 * is going to be removed or added it will have to wait. 1573 */ 1574 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1575 /* 1576 * refresh sensor data via sme_envsys_refresh_sensor 1577 */ 1578 mutex_enter(&sme->sme_mtx); 1579 sysmon_envsys_refresh_sensor(sme, edata); 1580 mutex_exit(&sme->sme_mtx); 1581 1582 /* 1583 * retrieve sensor's dictionary. 1584 */ 1585 dict = prop_array_get(array, edata->sensor); 1586 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) { 1587 DPRINTF(("%s: not a dictionary (%d:%s)\n", 1588 __func__, edata->sensor, sme->sme_name)); 1589 return EINVAL; 1590 } 1591 1592 /* 1593 * update sensor's state. 1594 */ 1595 error = sme_update_sensor_dictionary(dict, edata, true); 1596 1597 if (error) 1598 break; 1599 } 1600 1601 return error; 1602 } 1603 1604 int 1605 sme_update_sensor_dictionary(prop_object_t dict, envsys_data_t *edata, 1606 bool value_update) 1607 { 1608 const struct sme_descr_entry *sdt; 1609 int error = 0; 1610 1611 sdt = sme_find_table_entry(SME_DESC_STATES, edata->state); 1612 if (sdt == NULL) { 1613 printf("sme_update_sensor_dictionary: can not update sensor " 1614 "state %d unknown\n", edata->state); 1615 return EINVAL; 1616 } 1617 1618 DPRINTFOBJ(("%s: sensor #%d type=%d (%s) flags=%d\n", __func__, 1619 edata->sensor, sdt->type, sdt->desc, edata->flags)); 1620 1621 error = sme_sensor_upstring(dict, "state", sdt->desc); 1622 if (error) 1623 return (-error); 1624 1625 /* 1626 * update sensor's type. 1627 */ 1628 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1629 1630 DPRINTFOBJ(("%s: sensor #%d units=%d (%s)\n", __func__, edata->sensor, 1631 sdt->type, sdt->desc)); 1632 1633 error = sme_sensor_upstring(dict, "type", sdt->desc); 1634 if (error) 1635 return (-error); 1636 1637 if (value_update) { 1638 /* 1639 * update sensor's current value. 1640 */ 1641 error = sme_sensor_upint32(dict, "cur-value", edata->value_cur); 1642 if (error) 1643 return error; 1644 } 1645 1646 /* 1647 * Battery charge and Indicator types do not 1648 * need the remaining objects, so skip them. 1649 */ 1650 if (edata->units == ENVSYS_INDICATOR || 1651 edata->units == ENVSYS_BATTERY_CHARGE) 1652 return error; 1653 1654 /* 1655 * update sensor flags. 1656 */ 1657 if (edata->flags & ENVSYS_FPERCENT) { 1658 error = sme_sensor_upbool(dict, "want-percentage", true); 1659 if (error) 1660 return error; 1661 } 1662 1663 if (value_update) { 1664 /* 1665 * update sensor's {max,min}-value. 1666 */ 1667 if (edata->flags & ENVSYS_FVALID_MAX) { 1668 error = sme_sensor_upint32(dict, "max-value", 1669 edata->value_max); 1670 if (error) 1671 return error; 1672 } 1673 1674 if (edata->flags & ENVSYS_FVALID_MIN) { 1675 error = sme_sensor_upint32(dict, "min-value", 1676 edata->value_min); 1677 if (error) 1678 return error; 1679 } 1680 1681 /* 1682 * update 'rpms' only for ENVSYS_SFANRPM sensors. 1683 */ 1684 if (edata->units == ENVSYS_SFANRPM) { 1685 error = sme_sensor_upuint32(dict, "rpms", edata->rpms); 1686 if (error) 1687 return error; 1688 } 1689 1690 /* 1691 * update 'rfact' only for ENVSYS_SVOLTS_[AD]C sensors. 1692 */ 1693 if (edata->units == ENVSYS_SVOLTS_AC || 1694 edata->units == ENVSYS_SVOLTS_DC) { 1695 error = sme_sensor_upint32(dict, "rfact", edata->rfact); 1696 if (error) 1697 return error; 1698 } 1699 } 1700 1701 /* 1702 * update 'drive-state' only for ENVSYS_DRIVE sensors. 1703 */ 1704 if (edata->units == ENVSYS_DRIVE) { 1705 sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES, 1706 edata->value_cur); 1707 error = sme_sensor_upstring(dict, "drive-state", sdt->desc); 1708 if (error) 1709 return error; 1710 } 1711 1712 /* 1713 * update 'battery-capacity' only for ENVSYS_BATTERY_CAPACITY 1714 * sensors. 1715 */ 1716 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1717 sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY, 1718 edata->value_cur); 1719 error = sme_sensor_upstring(dict, "battery-capacity", 1720 sdt->desc); 1721 if (error) 1722 return error; 1723 } 1724 1725 return error; 1726 } 1727 1728 /* 1729 * sme_userset_dictionary: 1730 * 1731 * + Parse the userland dictionary and run the appropiate tasks 1732 * that were specified. 1733 */ 1734 int 1735 sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict, 1736 prop_array_t array) 1737 { 1738 const struct sme_descr_entry *sdt; 1739 envsys_data_t *edata; 1740 prop_dictionary_t dict, tdict = NULL; 1741 prop_object_t obj, obj1, obj2, tobj = NULL; 1742 uint32_t props; 1743 uint64_t refresh_timo = 0; 1744 sysmon_envsys_lim_t lims; 1745 int i, error = 0; 1746 const char *blah; 1747 bool targetfound = false; 1748 1749 /* 1750 * The user wanted to change the refresh timeout value for this 1751 * device. 1752 * 1753 * Get the 'device-properties' object from the userland dictionary. 1754 */ 1755 obj = prop_dictionary_get(udict, "device-properties"); 1756 if (obj && prop_object_type(obj) == PROP_TYPE_DICTIONARY) { 1757 /* 1758 * Get the 'refresh-timeout' property for this device. 1759 */ 1760 obj1 = prop_dictionary_get(obj, "refresh-timeout"); 1761 if (obj1 && prop_object_type(obj1) == PROP_TYPE_NUMBER) { 1762 targetfound = true; 1763 refresh_timo = 1764 prop_number_unsigned_integer_value(obj1); 1765 if (refresh_timo < 1) 1766 error = EINVAL; 1767 else { 1768 mutex_enter(&sme->sme_mtx); 1769 if (sme->sme_events_timeout != refresh_timo) { 1770 sme->sme_events_timeout = refresh_timo; 1771 sme_schedule_callout(sme); 1772 } 1773 mutex_exit(&sme->sme_mtx); 1774 } 1775 } 1776 return error; 1777 1778 } else if (!obj) { 1779 /* 1780 * Get sensor's index from userland dictionary. 1781 */ 1782 obj = prop_dictionary_get(udict, "index"); 1783 if (!obj) 1784 return EINVAL; 1785 if (prop_object_type(obj) != PROP_TYPE_STRING) { 1786 DPRINTF(("%s: 'index' not a string\n", __func__)); 1787 return EINVAL; 1788 } 1789 } else 1790 return EINVAL; 1791 1792 /* 1793 * Don't bother with locking when traversing the queue, 1794 * the device is already marked as busy; if a sensor 1795 * is going to be removed or added it will have to wait. 1796 */ 1797 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1798 /* 1799 * Get a dictionary and check if it's our sensor by checking 1800 * at its index position. 1801 */ 1802 dict = prop_array_get(array, edata->sensor); 1803 obj1 = prop_dictionary_get(dict, "index"); 1804 1805 /* 1806 * is it our sensor? 1807 */ 1808 if (!prop_string_equals(obj1, obj)) 1809 continue; 1810 1811 props = 0; 1812 1813 /* 1814 * Check if a new description operation was 1815 * requested by the user and set new description. 1816 */ 1817 obj2 = prop_dictionary_get(udict, "description"); 1818 if (obj2 && prop_object_type(obj2) == PROP_TYPE_STRING) { 1819 targetfound = true; 1820 blah = prop_string_cstring_nocopy(obj2); 1821 1822 /* 1823 * Check for duplicate description. 1824 */ 1825 for (i = 0; i < sme->sme_nsensors; i++) { 1826 if (i == edata->sensor) 1827 continue; 1828 tdict = prop_array_get(array, i); 1829 tobj = 1830 prop_dictionary_get(tdict, "description"); 1831 if (prop_string_equals(obj2, tobj)) { 1832 error = EEXIST; 1833 goto out; 1834 } 1835 } 1836 1837 /* 1838 * Update the object in dictionary. 1839 */ 1840 mutex_enter(&sme->sme_mtx); 1841 error = sme_sensor_upstring(dict, 1842 "description", 1843 blah); 1844 if (error) { 1845 mutex_exit(&sme->sme_mtx); 1846 goto out; 1847 } 1848 1849 DPRINTF(("%s: sensor%d changed desc to: %s\n", 1850 __func__, edata->sensor, blah)); 1851 edata->upropset |= PROP_DESC; 1852 mutex_exit(&sme->sme_mtx); 1853 } 1854 1855 /* 1856 * did the user want to change the rfact? 1857 */ 1858 obj2 = prop_dictionary_get(udict, "rfact"); 1859 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1860 targetfound = true; 1861 if (edata->flags & ENVSYS_FCHANGERFACT) { 1862 mutex_enter(&sme->sme_mtx); 1863 edata->rfact = prop_number_integer_value(obj2); 1864 edata->upropset |= PROP_RFACT; 1865 mutex_exit(&sme->sme_mtx); 1866 DPRINTF(("%s: sensor%d changed rfact to %d\n", 1867 __func__, edata->sensor, edata->rfact)); 1868 } else { 1869 error = ENOTSUP; 1870 goto out; 1871 } 1872 } 1873 1874 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1875 1876 /* 1877 * did the user want to set a critical capacity event? 1878 */ 1879 obj2 = prop_dictionary_get(udict, "critical-capacity"); 1880 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1881 targetfound = true; 1882 lims.sel_critmin = prop_number_integer_value(obj2); 1883 props |= PROP_BATTCAP; 1884 } 1885 1886 /* 1887 * did the user want to set a warning capacity event? 1888 */ 1889 obj2 = prop_dictionary_get(udict, "warning-capacity"); 1890 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1891 targetfound = true; 1892 lims.sel_warnmin = prop_number_integer_value(obj2); 1893 props |= PROP_BATTWARN; 1894 } 1895 1896 /* 1897 * did the user want to set a high capacity event? 1898 */ 1899 obj2 = prop_dictionary_get(udict, "high-capacity"); 1900 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1901 targetfound = true; 1902 lims.sel_warnmin = prop_number_integer_value(obj2); 1903 props |= PROP_BATTHIGH; 1904 } 1905 1906 /* 1907 * did the user want to set a maximum capacity event? 1908 */ 1909 obj2 = prop_dictionary_get(udict, "maximum-capacity"); 1910 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1911 targetfound = true; 1912 lims.sel_warnmin = prop_number_integer_value(obj2); 1913 props |= PROP_BATTMAX; 1914 } 1915 1916 /* 1917 * did the user want to set a critical max event? 1918 */ 1919 obj2 = prop_dictionary_get(udict, "critical-max"); 1920 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1921 targetfound = true; 1922 lims.sel_critmax = prop_number_integer_value(obj2); 1923 props |= PROP_CRITMAX; 1924 } 1925 1926 /* 1927 * did the user want to set a warning max event? 1928 */ 1929 obj2 = prop_dictionary_get(udict, "warning-max"); 1930 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1931 targetfound = true; 1932 lims.sel_warnmax = prop_number_integer_value(obj2); 1933 props |= PROP_WARNMAX; 1934 } 1935 1936 /* 1937 * did the user want to set a critical min event? 1938 */ 1939 obj2 = prop_dictionary_get(udict, "critical-min"); 1940 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1941 targetfound = true; 1942 lims.sel_critmin = prop_number_integer_value(obj2); 1943 props |= PROP_CRITMIN; 1944 } 1945 1946 /* 1947 * did the user want to set a warning min event? 1948 */ 1949 obj2 = prop_dictionary_get(udict, "warning-min"); 1950 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1951 targetfound = true; 1952 lims.sel_warnmin = prop_number_integer_value(obj2); 1953 props |= PROP_WARNMIN; 1954 } 1955 1956 if (props && (edata->flags & ENVSYS_FMONNOTSUPP) != 0) { 1957 error = ENOTSUP; 1958 goto out; 1959 } 1960 if (props || (edata->flags & ENVSYS_FHAS_ENTROPY) != 0) { 1961 error = sme_event_register(dict, edata, sme, &lims, 1962 props, 1963 (edata->flags & ENVSYS_FPERCENT)? 1964 PENVSYS_EVENT_CAPACITY: 1965 PENVSYS_EVENT_LIMITS, 1966 sdt->crittype); 1967 if (error == EEXIST) 1968 error = 0; 1969 if (error) 1970 goto out; 1971 } 1972 1973 /* 1974 * All objects in dictionary were processed. 1975 */ 1976 break; 1977 } 1978 1979 out: 1980 /* 1981 * invalid target? return the error. 1982 */ 1983 if (!targetfound) 1984 error = EINVAL; 1985 1986 return error; 1987 } 1988 1989 /* 1990 * + sysmon_envsys_foreach_sensor 1991 * 1992 * Walk through the devices' sensor lists and execute the callback. 1993 * If the callback returns false, the remainder of the current 1994 * device's sensors are skipped. 1995 */ 1996 void 1997 sysmon_envsys_foreach_sensor(sysmon_envsys_callback_t func, void *arg, 1998 bool refresh) 1999 { 2000 struct sysmon_envsys *sme; 2001 envsys_data_t *sensor; 2002 2003 mutex_enter(&sme_global_mtx); 2004 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 2005 2006 sysmon_envsys_acquire(sme, false); 2007 TAILQ_FOREACH(sensor, &sme->sme_sensors_list, sensors_head) { 2008 if (refresh) { 2009 mutex_enter(&sme->sme_mtx); 2010 sysmon_envsys_refresh_sensor(sme, sensor); 2011 mutex_exit(&sme->sme_mtx); 2012 } 2013 if (!(*func)(sme, sensor, arg)) 2014 break; 2015 } 2016 sysmon_envsys_release(sme, false); 2017 } 2018 mutex_exit(&sme_global_mtx); 2019 } 2020 2021 /* 2022 * Call the sensor's refresh function, and collect/stir entropy 2023 */ 2024 void 2025 sysmon_envsys_refresh_sensor(struct sysmon_envsys *sme, envsys_data_t *edata) 2026 { 2027 2028 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 2029 (*sme->sme_refresh)(sme, edata); 2030 2031 if (edata->flags & ENVSYS_FHAS_ENTROPY && 2032 edata->state != ENVSYS_SINVALID && 2033 edata->value_prev != edata->value_cur) 2034 rnd_add_uint32(&edata->rnd_src, edata->value_cur); 2035 edata->value_prev = edata->value_cur; 2036 } 2037