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