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