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