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