1 /* $NetBSD: sysmon_envsys.c,v 1.126 2012/11/29 10:29:45 msaitoh 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.126 2012/11/29 10:29:45 msaitoh 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/rnd.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 cv_init(&sme->sme_condvar, "sme_wait"); 489 490 return sme; 491 } 492 493 /* 494 * sysmon_envsys_destroy: 495 * 496 * + Removes all sensors from the tail queue, destroys the callout 497 * and frees the sysmon_envsys object. 498 */ 499 void 500 sysmon_envsys_destroy(struct sysmon_envsys *sme) 501 { 502 envsys_data_t *edata; 503 504 KASSERT(sme != NULL); 505 506 while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { 507 edata = TAILQ_FIRST(&sme->sme_sensors_list); 508 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 509 } 510 mutex_destroy(&sme->sme_mtx); 511 cv_destroy(&sme->sme_condvar); 512 kmem_free(sme, sizeof(*sme)); 513 } 514 515 /* 516 * sysmon_envsys_sensor_attach: 517 * 518 * + Attaches a sensor into a sysmon_envsys device checking that units 519 * is set to a valid type and description is unique and not empty. 520 */ 521 int 522 sysmon_envsys_sensor_attach(struct sysmon_envsys *sme, envsys_data_t *edata) 523 { 524 const struct sme_descr_entry *sdt_units; 525 envsys_data_t *oedata; 526 527 KASSERT(sme != NULL || edata != NULL); 528 529 /* 530 * Find the correct units for this sensor. 531 */ 532 sdt_units = sme_find_table_entry(SME_DESC_UNITS, edata->units); 533 if (sdt_units->type == -1) 534 return EINVAL; 535 536 /* 537 * Check that description is not empty or duplicate. 538 */ 539 if (strlen(edata->desc) == 0) 540 return EINVAL; 541 542 mutex_enter(&sme->sme_mtx); 543 sysmon_envsys_acquire(sme, true); 544 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 545 if (strcmp(oedata->desc, edata->desc) == 0) { 546 sysmon_envsys_release(sme, true); 547 mutex_exit(&sme->sme_mtx); 548 return EEXIST; 549 } 550 } 551 /* 552 * Ok, the sensor has been added into the device queue. 553 */ 554 TAILQ_INSERT_TAIL(&sme->sme_sensors_list, edata, sensors_head); 555 556 /* 557 * Give the sensor an index position. 558 */ 559 edata->sensor = sme->sme_nsensors; 560 sme->sme_nsensors++; 561 sysmon_envsys_release(sme, true); 562 mutex_exit(&sme->sme_mtx); 563 564 DPRINTF(("%s: attached #%d (%s), units=%d (%s)\n", 565 __func__, edata->sensor, edata->desc, 566 sdt_units->type, sdt_units->desc)); 567 568 return 0; 569 } 570 571 /* 572 * sysmon_envsys_sensor_detach: 573 * 574 * + Detachs a sensor from a sysmon_envsys device and decrements the 575 * sensors count on success. 576 */ 577 int 578 sysmon_envsys_sensor_detach(struct sysmon_envsys *sme, envsys_data_t *edata) 579 { 580 envsys_data_t *oedata; 581 bool found = false; 582 583 KASSERT(sme != NULL || edata != NULL); 584 585 /* 586 * Check the sensor is already on the list. 587 */ 588 mutex_enter(&sme->sme_mtx); 589 sysmon_envsys_acquire(sme, true); 590 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 591 if (oedata->sensor == edata->sensor) { 592 found = true; 593 break; 594 } 595 } 596 597 if (!found) { 598 sysmon_envsys_release(sme, true); 599 mutex_exit(&sme->sme_mtx); 600 return EINVAL; 601 } 602 603 /* 604 * remove it, unhook from rnd(4), and decrement the sensors count. 605 */ 606 sme_event_unregister_sensor(sme, edata); 607 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 608 sme->sme_nsensors--; 609 sysmon_envsys_release(sme, true); 610 mutex_exit(&sme->sme_mtx); 611 612 return 0; 613 } 614 615 616 /* 617 * sysmon_envsys_register: 618 * 619 * + Register a sysmon envsys device. 620 * + Create array of dictionaries for a device. 621 */ 622 int 623 sysmon_envsys_register(struct sysmon_envsys *sme) 624 { 625 struct sme_evdrv { 626 SLIST_ENTRY(sme_evdrv) evdrv_head; 627 sme_event_drv_t *evdrv; 628 }; 629 SLIST_HEAD(, sme_evdrv) sme_evdrv_list; 630 struct sme_evdrv *evdv = NULL; 631 struct sysmon_envsys *lsme; 632 prop_array_t array = NULL; 633 prop_dictionary_t dict, dict2; 634 envsys_data_t *edata = NULL; 635 sme_event_drv_t *this_evdrv; 636 int nevent; 637 int error = 0; 638 char rnd_name[sizeof(edata->rnd_src.name)]; 639 640 KASSERT(sme != NULL); 641 KASSERT(sme->sme_name != NULL); 642 643 /* 644 * Check if requested sysmon_envsys device is valid 645 * and does not exist already in the list. 646 */ 647 mutex_enter(&sme_global_mtx); 648 LIST_FOREACH(lsme, &sysmon_envsys_list, sme_list) { 649 if (strcmp(lsme->sme_name, sme->sme_name) == 0) { 650 mutex_exit(&sme_global_mtx); 651 return EEXIST; 652 } 653 } 654 mutex_exit(&sme_global_mtx); 655 656 /* 657 * sanity check: if SME_DISABLE_REFRESH is not set, 658 * the sme_refresh function callback must be non NULL. 659 */ 660 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 661 if (!sme->sme_refresh) 662 return EINVAL; 663 664 /* 665 * If the list of sensors is empty, there's no point to continue... 666 */ 667 if (TAILQ_EMPTY(&sme->sme_sensors_list)) { 668 DPRINTF(("%s: sensors list empty for %s\n", __func__, 669 sme->sme_name)); 670 return ENOTSUP; 671 } 672 673 /* 674 * Initialize the singly linked list for driver events. 675 */ 676 SLIST_INIT(&sme_evdrv_list); 677 678 array = prop_array_create(); 679 if (!array) 680 return ENOMEM; 681 682 /* 683 * Iterate over all sensors and create a dictionary per sensor. 684 * We must respect the order in which the sensors were added. 685 */ 686 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 687 dict = prop_dictionary_create(); 688 if (!dict) { 689 error = ENOMEM; 690 goto out2; 691 } 692 693 /* 694 * Create all objects in sensor's dictionary. 695 */ 696 this_evdrv = sme_add_sensor_dictionary(sme, array, 697 dict, edata); 698 if (this_evdrv) { 699 evdv = kmem_zalloc(sizeof(*evdv), KM_SLEEP); 700 evdv->evdrv = this_evdrv; 701 SLIST_INSERT_HEAD(&sme_evdrv_list, evdv, evdrv_head); 702 } 703 } 704 705 /* 706 * If the array does not contain any object (sensor), there's 707 * no need to attach the driver. 708 */ 709 if (prop_array_count(array) == 0) { 710 error = EINVAL; 711 DPRINTF(("%s: empty array for '%s'\n", __func__, 712 sme->sme_name)); 713 goto out; 714 } 715 716 /* 717 * Add the dictionary for the global properties of this device. 718 */ 719 dict2 = prop_dictionary_create(); 720 if (!dict2) { 721 error = ENOMEM; 722 goto out; 723 } 724 725 error = sme_add_property_dictionary(sme, array, dict2); 726 if (error) { 727 prop_object_release(dict2); 728 goto out; 729 } 730 731 /* 732 * Add the array into the global dictionary for the driver. 733 * 734 * <dict> 735 * <key>foo0</key> 736 * <array> 737 * ... 738 */ 739 mutex_enter(&sme_global_mtx); 740 if (!prop_dictionary_set(sme_propd, sme->sme_name, array)) { 741 error = EINVAL; 742 DPRINTF(("%s: prop_dictionary_set for '%s'\n", __func__, 743 sme->sme_name)); 744 goto out; 745 } 746 747 /* 748 * Add the device into the list. 749 */ 750 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); 751 sme->sme_fsensor = sysmon_envsys_next_sensor_index; 752 sysmon_envsys_next_sensor_index += sme->sme_nsensors; 753 mutex_exit(&sme_global_mtx); 754 755 out: 756 /* 757 * No errors? Make an initial data refresh if was requested, 758 * then register the events that were set in the driver. Do 759 * the refresh first in case it is needed to establish the 760 * limits or max_value needed by some events. 761 */ 762 if (error == 0) { 763 nevent = 0; 764 sysmon_task_queue_init(); 765 766 if (sme->sme_flags & SME_INIT_REFRESH) { 767 sysmon_task_queue_sched(0, sme_initial_refresh, sme); 768 DPRINTF(("%s: scheduled initial refresh for '%s'\n", 769 __func__, sme->sme_name)); 770 } 771 SLIST_FOREACH(evdv, &sme_evdrv_list, evdrv_head) { 772 sysmon_task_queue_sched(0, 773 sme_event_drvadd, evdv->evdrv); 774 nevent++; 775 } 776 /* 777 * Hook the sensor into rnd(4) entropy pool if requested 778 */ 779 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 780 if (edata->flags & ENVSYS_FHAS_ENTROPY) { 781 size_t n; 782 int tail = 1; 783 784 snprintf(rnd_name, sizeof(rnd_name), "%s-%s", 785 sme->sme_name, edata->desc); 786 n = strlen(rnd_name); 787 /* 788 * 1) Remove trailing white space(s). 789 * 2) If space exist, replace it with '-' 790 */ 791 while (--n) { 792 if (rnd_name[n] == ' ') { 793 if (tail != 0) 794 rnd_name[n] = '\0'; 795 else 796 rnd_name[n] = '-'; 797 } else 798 tail = 0; 799 } 800 rnd_attach_source(&edata->rnd_src, rnd_name, 801 RND_TYPE_ENV, 0); 802 } 803 } 804 DPRINTF(("%s: driver '%s' registered (nsens=%d nevent=%d)\n", 805 __func__, sme->sme_name, sme->sme_nsensors, nevent)); 806 } 807 808 out2: 809 while (!SLIST_EMPTY(&sme_evdrv_list)) { 810 evdv = SLIST_FIRST(&sme_evdrv_list); 811 SLIST_REMOVE_HEAD(&sme_evdrv_list, evdrv_head); 812 kmem_free(evdv, sizeof(*evdv)); 813 } 814 if (!error) 815 return 0; 816 817 /* 818 * Ugh... something wasn't right; unregister all events and sensors 819 * previously assigned and destroy the array with all its objects. 820 */ 821 DPRINTF(("%s: failed to register '%s' (%d)\n", __func__, 822 sme->sme_name, error)); 823 824 sme_event_unregister_all(sme); 825 while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { 826 edata = TAILQ_FIRST(&sme->sme_sensors_list); 827 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 828 } 829 sysmon_envsys_destroy_plist(array); 830 return error; 831 } 832 833 /* 834 * sysmon_envsys_destroy_plist: 835 * 836 * + Remove all objects from the array of dictionaries that is 837 * created in a sysmon envsys device. 838 */ 839 static void 840 sysmon_envsys_destroy_plist(prop_array_t array) 841 { 842 prop_object_iterator_t iter, iter2; 843 prop_dictionary_t dict; 844 prop_object_t obj; 845 846 KASSERT(array != NULL); 847 KASSERT(prop_object_type(array) == PROP_TYPE_ARRAY); 848 849 DPRINTFOBJ(("%s: objects in array=%d\n", __func__, 850 prop_array_count(array))); 851 852 iter = prop_array_iterator(array); 853 if (!iter) 854 return; 855 856 while ((dict = prop_object_iterator_next(iter))) { 857 KASSERT(prop_object_type(dict) == PROP_TYPE_DICTIONARY); 858 iter2 = prop_dictionary_iterator(dict); 859 if (!iter2) 860 goto out; 861 DPRINTFOBJ(("%s: iterating over dictionary\n", __func__)); 862 while ((obj = prop_object_iterator_next(iter2)) != NULL) { 863 DPRINTFOBJ(("%s: obj=%s\n", __func__, 864 prop_dictionary_keysym_cstring_nocopy(obj))); 865 prop_dictionary_remove(dict, 866 prop_dictionary_keysym_cstring_nocopy(obj)); 867 prop_object_iterator_reset(iter2); 868 } 869 prop_object_iterator_release(iter2); 870 DPRINTFOBJ(("%s: objects in dictionary:%d\n", 871 __func__, prop_dictionary_count(dict))); 872 prop_object_release(dict); 873 } 874 875 out: 876 prop_object_iterator_release(iter); 877 prop_object_release(array); 878 } 879 880 /* 881 * sysmon_envsys_unregister: 882 * 883 * + Unregister a sysmon envsys device. 884 */ 885 void 886 sysmon_envsys_unregister(struct sysmon_envsys *sme) 887 { 888 prop_array_t array; 889 struct sysmon_envsys *osme; 890 891 KASSERT(sme != NULL); 892 893 /* 894 * Unregister all events associated with device. 895 */ 896 sme_event_unregister_all(sme); 897 /* 898 * Decrement global sensors counter and the first_sensor index 899 * for remaining devices in the list (only used for compatibility 900 * with previous API), and remove the device from the list. 901 */ 902 mutex_enter(&sme_global_mtx); 903 sysmon_envsys_next_sensor_index -= sme->sme_nsensors; 904 LIST_FOREACH(osme, &sysmon_envsys_list, sme_list) { 905 if (osme->sme_fsensor >= sme->sme_fsensor) 906 osme->sme_fsensor -= sme->sme_nsensors; 907 } 908 LIST_REMOVE(sme, sme_list); 909 mutex_exit(&sme_global_mtx); 910 911 /* 912 * Remove the device (and all its objects) from the global dictionary. 913 */ 914 array = prop_dictionary_get(sme_propd, sme->sme_name); 915 if (array && prop_object_type(array) == PROP_TYPE_ARRAY) { 916 mutex_enter(&sme_global_mtx); 917 prop_dictionary_remove(sme_propd, sme->sme_name); 918 mutex_exit(&sme_global_mtx); 919 sysmon_envsys_destroy_plist(array); 920 } 921 /* 922 * And finally destroy the sysmon_envsys object. 923 */ 924 sysmon_envsys_destroy(sme); 925 } 926 927 /* 928 * sysmon_envsys_find: 929 * 930 * + Find a sysmon envsys device and mark it as busy 931 * once it's available. 932 */ 933 struct sysmon_envsys * 934 sysmon_envsys_find(const char *name) 935 { 936 struct sysmon_envsys *sme; 937 938 mutex_enter(&sme_global_mtx); 939 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 940 if (strcmp(sme->sme_name, name) == 0) { 941 sysmon_envsys_acquire(sme, false); 942 break; 943 } 944 } 945 mutex_exit(&sme_global_mtx); 946 947 return sme; 948 } 949 950 /* 951 * Compatibility function with the old API. 952 */ 953 struct sysmon_envsys * 954 sysmon_envsys_find_40(u_int idx) 955 { 956 struct sysmon_envsys *sme; 957 958 mutex_enter(&sme_global_mtx); 959 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 960 if (idx >= sme->sme_fsensor && 961 idx < (sme->sme_fsensor + sme->sme_nsensors)) { 962 sysmon_envsys_acquire(sme, false); 963 break; 964 } 965 } 966 mutex_exit(&sme_global_mtx); 967 968 return sme; 969 } 970 971 /* 972 * sysmon_envsys_acquire: 973 * 974 * + Wait until a sysmon envsys device is available and mark 975 * it as busy. 976 */ 977 void 978 sysmon_envsys_acquire(struct sysmon_envsys *sme, bool locked) 979 { 980 KASSERT(sme != NULL); 981 982 if (locked) { 983 while (sme->sme_flags & SME_FLAG_BUSY) 984 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 985 sme->sme_flags |= SME_FLAG_BUSY; 986 } else { 987 mutex_enter(&sme->sme_mtx); 988 while (sme->sme_flags & SME_FLAG_BUSY) 989 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 990 sme->sme_flags |= SME_FLAG_BUSY; 991 mutex_exit(&sme->sme_mtx); 992 } 993 } 994 995 /* 996 * sysmon_envsys_release: 997 * 998 * + Unmark a sysmon envsys device as busy, and notify 999 * waiters. 1000 */ 1001 void 1002 sysmon_envsys_release(struct sysmon_envsys *sme, bool locked) 1003 { 1004 KASSERT(sme != NULL); 1005 1006 if (locked) { 1007 sme->sme_flags &= ~SME_FLAG_BUSY; 1008 cv_broadcast(&sme->sme_condvar); 1009 } else { 1010 mutex_enter(&sme->sme_mtx); 1011 sme->sme_flags &= ~SME_FLAG_BUSY; 1012 cv_broadcast(&sme->sme_condvar); 1013 mutex_exit(&sme->sme_mtx); 1014 } 1015 } 1016 1017 /* 1018 * sme_initial_refresh: 1019 * 1020 * + Do an initial refresh of the sensors in a device just after 1021 * interrupts are enabled in the autoconf(9) process. 1022 * 1023 */ 1024 static void 1025 sme_initial_refresh(void *arg) 1026 { 1027 struct sysmon_envsys *sme = arg; 1028 envsys_data_t *edata; 1029 1030 mutex_enter(&sme->sme_mtx); 1031 sysmon_envsys_acquire(sme, true); 1032 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) 1033 sysmon_envsys_refresh_sensor(sme, edata); 1034 sysmon_envsys_release(sme, true); 1035 mutex_exit(&sme->sme_mtx); 1036 } 1037 1038 /* 1039 * sme_sensor_dictionary_get: 1040 * 1041 * + Returns a dictionary of a device specified by its index 1042 * position. 1043 */ 1044 prop_dictionary_t 1045 sme_sensor_dictionary_get(prop_array_t array, const char *index) 1046 { 1047 prop_object_iterator_t iter; 1048 prop_dictionary_t dict; 1049 prop_object_t obj; 1050 1051 KASSERT(array != NULL || index != NULL); 1052 1053 iter = prop_array_iterator(array); 1054 if (!iter) 1055 return NULL; 1056 1057 while ((dict = prop_object_iterator_next(iter))) { 1058 obj = prop_dictionary_get(dict, "index"); 1059 if (prop_string_equals_cstring(obj, index)) 1060 break; 1061 } 1062 1063 prop_object_iterator_release(iter); 1064 return dict; 1065 } 1066 1067 /* 1068 * sme_remove_userprops: 1069 * 1070 * + Remove all properties from all devices that were set by 1071 * the ENVSYS_SETDICTIONARY ioctl. 1072 */ 1073 static void 1074 sme_remove_userprops(void) 1075 { 1076 struct sysmon_envsys *sme; 1077 prop_array_t array; 1078 prop_dictionary_t sdict; 1079 envsys_data_t *edata = NULL; 1080 char tmp[ENVSYS_DESCLEN]; 1081 char rnd_name[sizeof(edata->rnd_src.name)]; 1082 sysmon_envsys_lim_t lims; 1083 const struct sme_descr_entry *sdt_units; 1084 uint32_t props; 1085 int ptype; 1086 1087 mutex_enter(&sme_global_mtx); 1088 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1089 sysmon_envsys_acquire(sme, false); 1090 array = prop_dictionary_get(sme_propd, sme->sme_name); 1091 1092 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1093 (void)snprintf(tmp, sizeof(tmp), "sensor%d", 1094 edata->sensor); 1095 sdict = sme_sensor_dictionary_get(array, tmp); 1096 KASSERT(sdict != NULL); 1097 1098 ptype = 0; 1099 if (edata->upropset & PROP_BATTCAP) { 1100 prop_dictionary_remove(sdict, 1101 "critical-capacity"); 1102 ptype = PENVSYS_EVENT_CAPACITY; 1103 } 1104 1105 if (edata->upropset & PROP_BATTWARN) { 1106 prop_dictionary_remove(sdict, 1107 "warning-capacity"); 1108 ptype = PENVSYS_EVENT_CAPACITY; 1109 } 1110 1111 if (edata->upropset & PROP_BATTHIGH) { 1112 prop_dictionary_remove(sdict, 1113 "high-capacity"); 1114 ptype = PENVSYS_EVENT_CAPACITY; 1115 } 1116 1117 if (edata->upropset & PROP_BATTMAX) { 1118 prop_dictionary_remove(sdict, 1119 "maximum-capacity"); 1120 ptype = PENVSYS_EVENT_CAPACITY; 1121 } 1122 if (edata->upropset & PROP_WARNMAX) { 1123 prop_dictionary_remove(sdict, "warning-max"); 1124 ptype = PENVSYS_EVENT_LIMITS; 1125 } 1126 1127 if (edata->upropset & PROP_WARNMIN) { 1128 prop_dictionary_remove(sdict, "warning-min"); 1129 ptype = PENVSYS_EVENT_LIMITS; 1130 } 1131 1132 if (edata->upropset & PROP_CRITMAX) { 1133 prop_dictionary_remove(sdict, "critical-max"); 1134 ptype = PENVSYS_EVENT_LIMITS; 1135 } 1136 1137 if (edata->upropset & PROP_CRITMIN) { 1138 prop_dictionary_remove(sdict, "critical-min"); 1139 ptype = PENVSYS_EVENT_LIMITS; 1140 } 1141 if (edata->upropset & PROP_RFACT) { 1142 (void)sme_sensor_upint32(sdict, "rfact", 0); 1143 edata->rfact = 0; 1144 } 1145 1146 if (edata->upropset & PROP_DESC) 1147 (void)sme_sensor_upstring(sdict, 1148 "description", edata->desc); 1149 1150 if (ptype == 0) 1151 continue; 1152 1153 /* 1154 * If there were any limit values removed, we 1155 * need to revert to initial limits. 1156 * 1157 * First, tell the driver that we need it to 1158 * restore any h/w limits which may have been 1159 * changed to stored, boot-time values. 1160 */ 1161 if (sme->sme_set_limits) { 1162 DPRINTF(("%s: reset limits for %s %s\n", 1163 __func__, sme->sme_name, edata->desc)); 1164 (*sme->sme_set_limits)(sme, edata, NULL, NULL); 1165 } 1166 1167 /* 1168 * Next, we need to retrieve those initial limits. 1169 */ 1170 props = 0; 1171 edata->upropset &= ~PROP_LIMITS; 1172 if (sme->sme_get_limits) { 1173 DPRINTF(("%s: retrieve limits for %s %s\n", 1174 __func__, sme->sme_name, edata->desc)); 1175 lims = edata->limits; 1176 (*sme->sme_get_limits)(sme, edata, &lims, 1177 &props); 1178 } 1179 1180 /* 1181 * Finally, remove any old limits event, then 1182 * install a new event (which will update the 1183 * dictionary) 1184 */ 1185 sme_event_unregister(sme, edata->desc, 1186 PENVSYS_EVENT_LIMITS); 1187 1188 /* 1189 * Find the correct units for this sensor. 1190 */ 1191 sdt_units = sme_find_table_entry(SME_DESC_UNITS, 1192 edata->units); 1193 1194 if (props & PROP_LIMITS) { 1195 DPRINTF(("%s: install limits for %s %s\n", 1196 __func__, sme->sme_name, edata->desc)); 1197 1198 sme_event_register(sdict, edata, sme, 1199 &lims, props, PENVSYS_EVENT_LIMITS, 1200 sdt_units->crittype); 1201 } 1202 if (edata->flags & ENVSYS_FHAS_ENTROPY) { 1203 sme_event_register(sdict, edata, sme, 1204 &lims, props, PENVSYS_EVENT_NULL, 1205 sdt_units->crittype); 1206 snprintf(rnd_name, sizeof(rnd_name), "%s-%s", 1207 sme->sme_name, edata->desc); 1208 rnd_attach_source(&edata->rnd_src, rnd_name, 1209 RND_TYPE_ENV, 0); 1210 } 1211 } 1212 1213 /* 1214 * Restore default timeout value. 1215 */ 1216 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1217 sme_schedule_callout(sme); 1218 sysmon_envsys_release(sme, false); 1219 } 1220 mutex_exit(&sme_global_mtx); 1221 } 1222 1223 /* 1224 * sme_add_property_dictionary: 1225 * 1226 * + Add global properties into a device. 1227 */ 1228 static int 1229 sme_add_property_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1230 prop_dictionary_t dict) 1231 { 1232 prop_dictionary_t pdict; 1233 const char *class; 1234 int error = 0; 1235 1236 pdict = prop_dictionary_create(); 1237 if (!pdict) 1238 return EINVAL; 1239 1240 /* 1241 * Add the 'refresh-timeout' and 'dev-class' objects into the 1242 * 'device-properties' dictionary. 1243 * 1244 * ... 1245 * <dict> 1246 * <key>device-properties</key> 1247 * <dict> 1248 * <key>refresh-timeout</key> 1249 * <integer>120</integer< 1250 * <key>device-class</key> 1251 * <string>class_name</string> 1252 * </dict> 1253 * </dict> 1254 * ... 1255 * 1256 */ 1257 if (sme->sme_events_timeout == 0) { 1258 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1259 sme_schedule_callout(sme); 1260 } 1261 1262 if (!prop_dictionary_set_uint64(pdict, "refresh-timeout", 1263 sme->sme_events_timeout)) { 1264 error = EINVAL; 1265 goto out; 1266 } 1267 if (sme->sme_class == SME_CLASS_BATTERY) 1268 class = "battery"; 1269 else if (sme->sme_class == SME_CLASS_ACADAPTER) 1270 class = "ac-adapter"; 1271 else 1272 class = "other"; 1273 if (!prop_dictionary_set_cstring_nocopy(pdict, "device-class", class)) { 1274 error = EINVAL; 1275 goto out; 1276 } 1277 1278 if (!prop_dictionary_set(dict, "device-properties", pdict)) { 1279 error = EINVAL; 1280 goto out; 1281 } 1282 1283 /* 1284 * Add the device dictionary into the sysmon envsys array. 1285 */ 1286 if (!prop_array_add(array, dict)) 1287 error = EINVAL; 1288 1289 out: 1290 prop_object_release(pdict); 1291 return error; 1292 } 1293 1294 /* 1295 * sme_add_sensor_dictionary: 1296 * 1297 * + Adds the sensor objects into the dictionary and returns a pointer 1298 * to a sme_event_drv_t object if a monitoring flag was set 1299 * (or NULL otherwise). 1300 */ 1301 static sme_event_drv_t * 1302 sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1303 prop_dictionary_t dict, envsys_data_t *edata) 1304 { 1305 const struct sme_descr_entry *sdt; 1306 int error; 1307 sme_event_drv_t *sme_evdrv_t = NULL; 1308 char indexstr[ENVSYS_DESCLEN]; 1309 bool mon_supported, allow_rfact; 1310 1311 /* 1312 * Add the index sensor string. 1313 * 1314 * ... 1315 * <key>index</eyr 1316 * <string>sensor0</string> 1317 * ... 1318 */ 1319 (void)snprintf(indexstr, sizeof(indexstr), "sensor%d", edata->sensor); 1320 if (sme_sensor_upstring(dict, "index", indexstr)) 1321 goto bad; 1322 1323 /* 1324 * ... 1325 * <key>description</key> 1326 * <string>blah blah</string> 1327 * ... 1328 */ 1329 if (sme_sensor_upstring(dict, "description", edata->desc)) 1330 goto bad; 1331 1332 /* 1333 * Add the monitoring boolean object: 1334 * 1335 * ... 1336 * <key>monitoring-supported</key> 1337 * <true/> 1338 * ... 1339 * 1340 * always false on Battery {capacity,charge}, Drive and Indicator types. 1341 * They cannot be monitored. 1342 * 1343 */ 1344 if ((edata->flags & ENVSYS_FMONNOTSUPP) || 1345 (edata->units == ENVSYS_INDICATOR) || 1346 (edata->units == ENVSYS_DRIVE) || 1347 (edata->units == ENVSYS_BATTERY_CAPACITY) || 1348 (edata->units == ENVSYS_BATTERY_CHARGE)) 1349 mon_supported = false; 1350 else 1351 mon_supported = true; 1352 if (sme_sensor_upbool(dict, "monitoring-supported", mon_supported)) 1353 goto out; 1354 1355 /* 1356 * Add the allow-rfact boolean object, true if 1357 * ENVSYS_FCHANGERFACT is set, false otherwise. 1358 * 1359 * ... 1360 * <key>allow-rfact</key> 1361 * <true/> 1362 * ... 1363 */ 1364 if (edata->units == ENVSYS_SVOLTS_DC || 1365 edata->units == ENVSYS_SVOLTS_AC) { 1366 if (edata->flags & ENVSYS_FCHANGERFACT) 1367 allow_rfact = true; 1368 else 1369 allow_rfact = false; 1370 if (sme_sensor_upbool(dict, "allow-rfact", allow_rfact)) 1371 goto out; 1372 } 1373 1374 error = sme_update_sensor_dictionary(dict, edata, 1375 (edata->state == ENVSYS_SVALID)); 1376 if (error < 0) 1377 goto bad; 1378 else if (error) 1379 goto out; 1380 1381 /* 1382 * ... 1383 * </dict> 1384 * 1385 * Add the dictionary into the array. 1386 * 1387 */ 1388 if (!prop_array_add(array, dict)) { 1389 DPRINTF(("%s: prop_array_add\n", __func__)); 1390 goto bad; 1391 } 1392 1393 /* 1394 * Register new event(s) if any monitoring flag was set or if 1395 * the sensor provides entropy for rnd(4). 1396 */ 1397 if (edata->flags & (ENVSYS_FMONANY | ENVSYS_FHAS_ENTROPY)) { 1398 sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP); 1399 sme_evdrv_t->sed_sdict = dict; 1400 sme_evdrv_t->sed_edata = edata; 1401 sme_evdrv_t->sed_sme = sme; 1402 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1403 sme_evdrv_t->sed_powertype = sdt->crittype; 1404 } 1405 1406 out: 1407 return sme_evdrv_t; 1408 1409 bad: 1410 prop_object_release(dict); 1411 return NULL; 1412 } 1413 1414 /* 1415 * Find the maximum of all currently reported values. 1416 * The provided callback decides whether a sensor is part of the 1417 * maximum calculation (by returning true) or ignored (callback 1418 * returns false). Example usage: callback selects temperature 1419 * sensors in a given thermal zone, the function calculates the 1420 * maximum currently reported temperature in this zone. 1421 * If the parameter "refresh" is true, new values will be aquired 1422 * from the hardware, if not, the last reported value will be used. 1423 */ 1424 uint32_t 1425 sysmon_envsys_get_max_value(bool (*predicate)(const envsys_data_t*), 1426 bool refresh) 1427 { 1428 struct sysmon_envsys *sme; 1429 uint32_t maxv, v; 1430 1431 maxv = 0; 1432 mutex_enter(&sme_global_mtx); 1433 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1434 sysmon_envsys_acquire(sme, false); 1435 v = sme_get_max_value(sme, predicate, refresh); 1436 sysmon_envsys_release(sme, false); 1437 if (v > maxv) 1438 maxv = v; 1439 } 1440 mutex_exit(&sme_global_mtx); 1441 return maxv; 1442 } 1443 1444 static uint32_t 1445 sme_get_max_value(struct sysmon_envsys *sme, 1446 bool (*predicate)(const envsys_data_t*), 1447 bool refresh) 1448 { 1449 envsys_data_t *edata; 1450 uint32_t maxv, v; 1451 1452 /* 1453 * Iterate over all sensors that match the predicate 1454 */ 1455 maxv = 0; 1456 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1457 if (!(*predicate)(edata)) 1458 continue; 1459 1460 /* 1461 * refresh sensor data 1462 */ 1463 mutex_enter(&sme->sme_mtx); 1464 sysmon_envsys_refresh_sensor(sme, edata); 1465 mutex_exit(&sme->sme_mtx); 1466 1467 v = edata->value_cur; 1468 if (v > maxv) 1469 maxv = v; 1470 1471 } 1472 1473 return maxv; 1474 } 1475 1476 /* 1477 * sme_update_dictionary: 1478 * 1479 * + Update per-sensor dictionaries with new values if there were 1480 * changes, otherwise the object in dictionary is untouched. 1481 */ 1482 int 1483 sme_update_dictionary(struct sysmon_envsys *sme) 1484 { 1485 envsys_data_t *edata; 1486 prop_object_t array, dict, obj, obj2; 1487 int error = 0; 1488 1489 /* 1490 * Retrieve the array of dictionaries in device. 1491 */ 1492 array = prop_dictionary_get(sme_propd, sme->sme_name); 1493 if (prop_object_type(array) != PROP_TYPE_ARRAY) { 1494 DPRINTF(("%s: not an array (%s)\n", __func__, sme->sme_name)); 1495 return EINVAL; 1496 } 1497 1498 /* 1499 * Get the last dictionary on the array, this contains the 1500 * 'device-properties' sub-dictionary. 1501 */ 1502 obj = prop_array_get(array, prop_array_count(array) - 1); 1503 if (!obj || prop_object_type(obj) != PROP_TYPE_DICTIONARY) { 1504 DPRINTF(("%s: not a device-properties dictionary\n", __func__)); 1505 return EINVAL; 1506 } 1507 1508 obj2 = prop_dictionary_get(obj, "device-properties"); 1509 if (!obj2) 1510 return EINVAL; 1511 1512 /* 1513 * Update the 'refresh-timeout' property. 1514 */ 1515 if (!prop_dictionary_set_uint64(obj2, "refresh-timeout", 1516 sme->sme_events_timeout)) 1517 return EINVAL; 1518 1519 /* 1520 * - iterate over all sensors. 1521 * - fetch new data. 1522 * - check if data in dictionary is different than new data. 1523 * - update dictionary if there were changes. 1524 */ 1525 DPRINTF(("%s: updating '%s' with nsensors=%d\n", __func__, 1526 sme->sme_name, sme->sme_nsensors)); 1527 1528 /* 1529 * Don't bother with locking when traversing the queue, 1530 * the device is already marked as busy; if a sensor 1531 * is going to be removed or added it will have to wait. 1532 */ 1533 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1534 /* 1535 * refresh sensor data via sme_envsys_refresh_sensor 1536 */ 1537 mutex_enter(&sme->sme_mtx); 1538 sysmon_envsys_refresh_sensor(sme, edata); 1539 mutex_exit(&sme->sme_mtx); 1540 1541 /* 1542 * retrieve sensor's dictionary. 1543 */ 1544 dict = prop_array_get(array, edata->sensor); 1545 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) { 1546 DPRINTF(("%s: not a dictionary (%d:%s)\n", 1547 __func__, edata->sensor, sme->sme_name)); 1548 return EINVAL; 1549 } 1550 1551 /* 1552 * update sensor's state. 1553 */ 1554 error = sme_update_sensor_dictionary(dict, edata, true); 1555 1556 if (error) 1557 break; 1558 } 1559 1560 return error; 1561 } 1562 1563 int 1564 sme_update_sensor_dictionary(prop_object_t dict, envsys_data_t *edata, 1565 bool value_update) 1566 { 1567 const struct sme_descr_entry *sdt; 1568 int error = 0; 1569 1570 sdt = sme_find_table_entry(SME_DESC_STATES, edata->state); 1571 if (sdt == NULL) { 1572 printf("sme_update_sensor_dictionary: can not update sensor " 1573 "state %d unknown\n", edata->state); 1574 return EINVAL; 1575 } 1576 1577 DPRINTFOBJ(("%s: sensor #%d type=%d (%s) flags=%d\n", __func__, 1578 edata->sensor, sdt->type, sdt->desc, edata->flags)); 1579 1580 error = sme_sensor_upstring(dict, "state", sdt->desc); 1581 if (error) 1582 return (-error); 1583 1584 /* 1585 * update sensor's type. 1586 */ 1587 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1588 1589 DPRINTFOBJ(("%s: sensor #%d units=%d (%s)\n", __func__, edata->sensor, 1590 sdt->type, sdt->desc)); 1591 1592 error = sme_sensor_upstring(dict, "type", sdt->desc); 1593 if (error) 1594 return (-error); 1595 1596 if (value_update) { 1597 /* 1598 * update sensor's current value. 1599 */ 1600 error = sme_sensor_upint32(dict, "cur-value", edata->value_cur); 1601 if (error) 1602 return error; 1603 } 1604 1605 /* 1606 * Battery charge and Indicator types do not 1607 * need the remaining objects, so skip them. 1608 */ 1609 if (edata->units == ENVSYS_INDICATOR || 1610 edata->units == ENVSYS_BATTERY_CHARGE) 1611 return error; 1612 1613 /* 1614 * update sensor flags. 1615 */ 1616 if (edata->flags & ENVSYS_FPERCENT) { 1617 error = sme_sensor_upbool(dict, "want-percentage", true); 1618 if (error) 1619 return error; 1620 } 1621 1622 if (value_update) { 1623 /* 1624 * update sensor's {max,min}-value. 1625 */ 1626 if (edata->flags & ENVSYS_FVALID_MAX) { 1627 error = sme_sensor_upint32(dict, "max-value", 1628 edata->value_max); 1629 if (error) 1630 return error; 1631 } 1632 1633 if (edata->flags & ENVSYS_FVALID_MIN) { 1634 error = sme_sensor_upint32(dict, "min-value", 1635 edata->value_min); 1636 if (error) 1637 return error; 1638 } 1639 1640 /* 1641 * update 'rpms' only for ENVSYS_SFANRPM sensors. 1642 */ 1643 if (edata->units == ENVSYS_SFANRPM) { 1644 error = sme_sensor_upuint32(dict, "rpms", edata->rpms); 1645 if (error) 1646 return error; 1647 } 1648 1649 /* 1650 * update 'rfact' only for ENVSYS_SVOLTS_[AD]C sensors. 1651 */ 1652 if (edata->units == ENVSYS_SVOLTS_AC || 1653 edata->units == ENVSYS_SVOLTS_DC) { 1654 error = sme_sensor_upint32(dict, "rfact", edata->rfact); 1655 if (error) 1656 return error; 1657 } 1658 } 1659 1660 /* 1661 * update 'drive-state' only for ENVSYS_DRIVE sensors. 1662 */ 1663 if (edata->units == ENVSYS_DRIVE) { 1664 sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES, 1665 edata->value_cur); 1666 error = sme_sensor_upstring(dict, "drive-state", sdt->desc); 1667 if (error) 1668 return error; 1669 } 1670 1671 /* 1672 * update 'battery-capacity' only for ENVSYS_BATTERY_CAPACITY 1673 * sensors. 1674 */ 1675 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1676 sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY, 1677 edata->value_cur); 1678 error = sme_sensor_upstring(dict, "battery-capacity", 1679 sdt->desc); 1680 if (error) 1681 return error; 1682 } 1683 1684 return error; 1685 } 1686 1687 /* 1688 * sme_userset_dictionary: 1689 * 1690 * + Parse the userland dictionary and run the appropiate tasks 1691 * that were specified. 1692 */ 1693 int 1694 sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict, 1695 prop_array_t array) 1696 { 1697 const struct sme_descr_entry *sdt; 1698 envsys_data_t *edata; 1699 prop_dictionary_t dict, tdict = NULL; 1700 prop_object_t obj, obj1, obj2, tobj = NULL; 1701 uint32_t props; 1702 uint64_t refresh_timo = 0; 1703 sysmon_envsys_lim_t lims; 1704 int i, error = 0; 1705 const char *blah; 1706 bool targetfound = false; 1707 1708 /* 1709 * The user wanted to change the refresh timeout value for this 1710 * device. 1711 * 1712 * Get the 'device-properties' object from the userland dictionary. 1713 */ 1714 obj = prop_dictionary_get(udict, "device-properties"); 1715 if (obj && prop_object_type(obj) == PROP_TYPE_DICTIONARY) { 1716 /* 1717 * Get the 'refresh-timeout' property for this device. 1718 */ 1719 obj1 = prop_dictionary_get(obj, "refresh-timeout"); 1720 if (obj1 && prop_object_type(obj1) == PROP_TYPE_NUMBER) { 1721 targetfound = true; 1722 refresh_timo = 1723 prop_number_unsigned_integer_value(obj1); 1724 if (refresh_timo < 1) 1725 error = EINVAL; 1726 else { 1727 mutex_enter(&sme->sme_mtx); 1728 if (sme->sme_events_timeout != refresh_timo) { 1729 sme->sme_events_timeout = refresh_timo; 1730 sme_schedule_callout(sme); 1731 } 1732 mutex_exit(&sme->sme_mtx); 1733 } 1734 } 1735 return error; 1736 1737 } else if (!obj) { 1738 /* 1739 * Get sensor's index from userland dictionary. 1740 */ 1741 obj = prop_dictionary_get(udict, "index"); 1742 if (!obj) 1743 return EINVAL; 1744 if (prop_object_type(obj) != PROP_TYPE_STRING) { 1745 DPRINTF(("%s: 'index' not a string\n", __func__)); 1746 return EINVAL; 1747 } 1748 } else 1749 return EINVAL; 1750 1751 /* 1752 * Don't bother with locking when traversing the queue, 1753 * the device is already marked as busy; if a sensor 1754 * is going to be removed or added it will have to wait. 1755 */ 1756 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1757 /* 1758 * Get a dictionary and check if it's our sensor by checking 1759 * at its index position. 1760 */ 1761 dict = prop_array_get(array, edata->sensor); 1762 obj1 = prop_dictionary_get(dict, "index"); 1763 1764 /* 1765 * is it our sensor? 1766 */ 1767 if (!prop_string_equals(obj1, obj)) 1768 continue; 1769 1770 props = 0; 1771 1772 /* 1773 * Check if a new description operation was 1774 * requested by the user and set new description. 1775 */ 1776 obj2 = prop_dictionary_get(udict, "description"); 1777 if (obj2 && prop_object_type(obj2) == PROP_TYPE_STRING) { 1778 targetfound = true; 1779 blah = prop_string_cstring_nocopy(obj2); 1780 1781 /* 1782 * Check for duplicate description. 1783 */ 1784 for (i = 0; i < sme->sme_nsensors; i++) { 1785 if (i == edata->sensor) 1786 continue; 1787 tdict = prop_array_get(array, i); 1788 tobj = 1789 prop_dictionary_get(tdict, "description"); 1790 if (prop_string_equals(obj2, tobj)) { 1791 error = EEXIST; 1792 goto out; 1793 } 1794 } 1795 1796 /* 1797 * Update the object in dictionary. 1798 */ 1799 mutex_enter(&sme->sme_mtx); 1800 error = sme_sensor_upstring(dict, 1801 "description", 1802 blah); 1803 if (error) { 1804 mutex_exit(&sme->sme_mtx); 1805 goto out; 1806 } 1807 1808 DPRINTF(("%s: sensor%d changed desc to: %s\n", 1809 __func__, edata->sensor, blah)); 1810 edata->upropset |= PROP_DESC; 1811 mutex_exit(&sme->sme_mtx); 1812 } 1813 1814 /* 1815 * did the user want to change the rfact? 1816 */ 1817 obj2 = prop_dictionary_get(udict, "rfact"); 1818 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1819 targetfound = true; 1820 if (edata->flags & ENVSYS_FCHANGERFACT) { 1821 mutex_enter(&sme->sme_mtx); 1822 edata->rfact = prop_number_integer_value(obj2); 1823 edata->upropset |= PROP_RFACT; 1824 mutex_exit(&sme->sme_mtx); 1825 DPRINTF(("%s: sensor%d changed rfact to %d\n", 1826 __func__, edata->sensor, edata->rfact)); 1827 } else { 1828 error = ENOTSUP; 1829 goto out; 1830 } 1831 } 1832 1833 sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); 1834 1835 /* 1836 * did the user want to set a critical capacity event? 1837 */ 1838 obj2 = prop_dictionary_get(udict, "critical-capacity"); 1839 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1840 targetfound = true; 1841 lims.sel_critmin = prop_number_integer_value(obj2); 1842 props |= PROP_BATTCAP; 1843 } 1844 1845 /* 1846 * did the user want to set a warning capacity event? 1847 */ 1848 obj2 = prop_dictionary_get(udict, "warning-capacity"); 1849 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1850 targetfound = true; 1851 lims.sel_warnmin = prop_number_integer_value(obj2); 1852 props |= PROP_BATTWARN; 1853 } 1854 1855 /* 1856 * did the user want to set a high capacity event? 1857 */ 1858 obj2 = prop_dictionary_get(udict, "high-capacity"); 1859 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1860 targetfound = true; 1861 lims.sel_warnmin = prop_number_integer_value(obj2); 1862 props |= PROP_BATTHIGH; 1863 } 1864 1865 /* 1866 * did the user want to set a maximum capacity event? 1867 */ 1868 obj2 = prop_dictionary_get(udict, "maximum-capacity"); 1869 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1870 targetfound = true; 1871 lims.sel_warnmin = prop_number_integer_value(obj2); 1872 props |= PROP_BATTMAX; 1873 } 1874 1875 /* 1876 * did the user want to set a critical max event? 1877 */ 1878 obj2 = prop_dictionary_get(udict, "critical-max"); 1879 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1880 targetfound = true; 1881 lims.sel_critmax = prop_number_integer_value(obj2); 1882 props |= PROP_CRITMAX; 1883 } 1884 1885 /* 1886 * did the user want to set a warning max event? 1887 */ 1888 obj2 = prop_dictionary_get(udict, "warning-max"); 1889 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1890 targetfound = true; 1891 lims.sel_warnmax = prop_number_integer_value(obj2); 1892 props |= PROP_WARNMAX; 1893 } 1894 1895 /* 1896 * did the user want to set a critical min event? 1897 */ 1898 obj2 = prop_dictionary_get(udict, "critical-min"); 1899 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1900 targetfound = true; 1901 lims.sel_critmin = prop_number_integer_value(obj2); 1902 props |= PROP_CRITMIN; 1903 } 1904 1905 /* 1906 * did the user want to set a warning min event? 1907 */ 1908 obj2 = prop_dictionary_get(udict, "warning-min"); 1909 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1910 targetfound = true; 1911 lims.sel_warnmin = prop_number_integer_value(obj2); 1912 props |= PROP_WARNMIN; 1913 } 1914 1915 if (props && (edata->flags & ENVSYS_FMONNOTSUPP) != 0) { 1916 error = ENOTSUP; 1917 goto out; 1918 } 1919 if (props || (edata->flags & ENVSYS_FHAS_ENTROPY) != 0) { 1920 error = sme_event_register(dict, edata, sme, &lims, 1921 props, 1922 (edata->flags & ENVSYS_FPERCENT)? 1923 PENVSYS_EVENT_CAPACITY: 1924 PENVSYS_EVENT_LIMITS, 1925 sdt->crittype); 1926 if (error == EEXIST) 1927 error = 0; 1928 if (error) 1929 goto out; 1930 } 1931 1932 /* 1933 * All objects in dictionary were processed. 1934 */ 1935 break; 1936 } 1937 1938 out: 1939 /* 1940 * invalid target? return the error. 1941 */ 1942 if (!targetfound) 1943 error = EINVAL; 1944 1945 return error; 1946 } 1947 1948 /* 1949 * + sysmon_envsys_foreach_sensor 1950 * 1951 * Walk through the devices' sensor lists and execute the callback. 1952 * If the callback returns false, the remainder of the current 1953 * device's sensors are skipped. 1954 */ 1955 void 1956 sysmon_envsys_foreach_sensor(sysmon_envsys_callback_t func, void *arg, 1957 bool refresh) 1958 { 1959 struct sysmon_envsys *sme; 1960 envsys_data_t *sensor; 1961 1962 mutex_enter(&sme_global_mtx); 1963 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1964 1965 sysmon_envsys_acquire(sme, false); 1966 TAILQ_FOREACH(sensor, &sme->sme_sensors_list, sensors_head) { 1967 if (refresh) { 1968 mutex_enter(&sme->sme_mtx); 1969 sysmon_envsys_refresh_sensor(sme, sensor); 1970 mutex_exit(&sme->sme_mtx); 1971 } 1972 if (!(*func)(sme, sensor, arg)) 1973 break; 1974 } 1975 sysmon_envsys_release(sme, false); 1976 } 1977 mutex_exit(&sme_global_mtx); 1978 } 1979 1980 /* 1981 * Call the sensor's refresh function, and collect/stir entropy 1982 */ 1983 void 1984 sysmon_envsys_refresh_sensor(struct sysmon_envsys *sme, envsys_data_t *edata) 1985 { 1986 1987 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 1988 (*sme->sme_refresh)(sme, edata); 1989 1990 if (edata->flags & ENVSYS_FHAS_ENTROPY && 1991 edata->state != ENVSYS_SINVALID && 1992 edata->value_prev != edata->value_cur) 1993 rnd_add_uint32(&edata->rnd_src, edata->value_cur); 1994 edata->value_prev = edata->value_cur; 1995 } 1996