1 /* $NetBSD: sysmon_envsys.c,v 1.96 2010/02/15 22:32:04 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.96 2010/02/15 22:32:04 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 1065 if (edata->upropset & PROP_BATTHIGH) { 1066 prop_dictionary_remove(sdict, 1067 "high-capacity"); 1068 ptype = PENVSYS_EVENT_CAPACITY; 1069 } 1070 1071 if (edata->upropset & PROP_BATTMAX) { 1072 prop_dictionary_remove(sdict, 1073 "maximum-capacity"); 1074 ptype = PENVSYS_EVENT_CAPACITY; 1075 } 1076 if (ptype != 0) 1077 sme_event_unregister(sme, edata->desc, ptype); 1078 1079 ptype = 0; 1080 if (edata->upropset & PROP_WARNMAX) { 1081 prop_dictionary_remove(sdict, "warning-max"); 1082 ptype = PENVSYS_EVENT_LIMITS; 1083 } 1084 1085 if (edata->upropset & PROP_WARNMIN) { 1086 prop_dictionary_remove(sdict, "warning-min"); 1087 ptype = PENVSYS_EVENT_LIMITS; 1088 } 1089 1090 if (edata->upropset & PROP_CRITMAX) { 1091 prop_dictionary_remove(sdict, "critical-max"); 1092 ptype = PENVSYS_EVENT_LIMITS; 1093 } 1094 1095 if (edata->upropset & PROP_CRITMIN) { 1096 prop_dictionary_remove(sdict, "critical-min"); 1097 ptype = PENVSYS_EVENT_LIMITS; 1098 } 1099 if (ptype != 0) 1100 sme_event_unregister(sme, edata->desc, ptype); 1101 1102 if (edata->upropset & PROP_RFACT) { 1103 (void)sme_sensor_upint32(sdict, "rfact", 0); 1104 edata->rfact = 0; 1105 } 1106 1107 if (edata->upropset & PROP_DESC) 1108 (void)sme_sensor_upstring(sdict, 1109 "description", edata->desc); 1110 1111 if (edata->upropset) 1112 edata->upropset = 0; 1113 } 1114 1115 /* 1116 * Restore default timeout value. 1117 */ 1118 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1119 sysmon_envsys_release(sme, false); 1120 } 1121 mutex_exit(&sme_global_mtx); 1122 } 1123 1124 /* 1125 * sme_add_property_dictionary: 1126 * 1127 * + Add global properties into a device. 1128 */ 1129 static int 1130 sme_add_property_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1131 prop_dictionary_t dict) 1132 { 1133 prop_dictionary_t pdict; 1134 int error = 0; 1135 1136 pdict = prop_dictionary_create(); 1137 if (!pdict) 1138 return EINVAL; 1139 1140 /* 1141 * Add the 'refresh-timeout' object into the 'device-properties' 1142 * dictionary. We use by default 30 seconds. 1143 * 1144 * ... 1145 * <dict> 1146 * <key>device-properties</key> 1147 * <dict> 1148 * <key>refresh-timeout</key> 1149 * <integer>120</integer< 1150 * </dict< 1151 * </dict> 1152 * ... 1153 * 1154 */ 1155 if (!sme->sme_events_timeout) 1156 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1157 1158 if (!prop_dictionary_set_uint64(pdict, "refresh-timeout", 1159 sme->sme_events_timeout)) { 1160 error = EINVAL; 1161 goto out; 1162 } 1163 1164 if (!prop_dictionary_set(dict, "device-properties", pdict)) { 1165 error = EINVAL; 1166 goto out; 1167 } 1168 1169 /* 1170 * Add the device dictionary into the sysmon envsys array. 1171 */ 1172 if (!prop_array_add(array, dict)) 1173 error = EINVAL; 1174 1175 out: 1176 prop_object_release(pdict); 1177 return error; 1178 } 1179 1180 /* 1181 * sme_add_sensor_dictionary: 1182 * 1183 * + Adds the sensor objects into the dictionary and returns a pointer 1184 * to a sme_event_drv_t object if a monitoring flag was set 1185 * (or NULL otherwise). 1186 */ 1187 sme_event_drv_t * 1188 sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1189 prop_dictionary_t dict, envsys_data_t *edata) 1190 { 1191 const struct sme_description_table *sdt, *sdt_units; 1192 sme_event_drv_t *sme_evdrv_t = NULL; 1193 int i, j; 1194 char indexstr[ENVSYS_DESCLEN]; 1195 1196 /* 1197 * Find the correct units for this sensor. 1198 */ 1199 sdt_units = sme_get_description_table(SME_DESC_UNITS); 1200 for (i = 0; sdt_units[i].type != -1; i++) 1201 if (sdt_units[i].type == edata->units) 1202 break; 1203 1204 /* 1205 * Add the index sensor string. 1206 * 1207 * ... 1208 * <key>index</eyr 1209 * <string>sensor0</string> 1210 * ... 1211 */ 1212 (void)snprintf(indexstr, sizeof(indexstr), "sensor%d", edata->sensor); 1213 if (sme_sensor_upstring(dict, "index", indexstr)) 1214 goto bad; 1215 1216 /* 1217 * ... 1218 * <key>type</key> 1219 * <string>foo</string> 1220 * <key>description</key> 1221 * <string>blah blah</string> 1222 * ... 1223 */ 1224 if (sme_sensor_upstring(dict, "type", sdt_units[i].desc)) 1225 goto bad; 1226 1227 if (sme_sensor_upstring(dict, "description", edata->desc)) 1228 goto bad; 1229 1230 /* 1231 * Add sensor's state description. 1232 * 1233 * ... 1234 * <key>state</key> 1235 * <string>valid</string> 1236 * ... 1237 */ 1238 sdt = sme_get_description_table(SME_DESC_STATES); 1239 for (j = 0; sdt[j].type != -1; j++) 1240 if (sdt[j].type == edata->state) 1241 break; 1242 1243 DPRINTF(("%s: sensor desc=%s type=%d state=%d\n", 1244 __func__, edata->desc, edata->units, edata->state)); 1245 1246 if (sme_sensor_upstring(dict, "state", sdt[j].desc)) 1247 goto bad; 1248 1249 /* 1250 * Add the monitoring boolean object: 1251 * 1252 * ... 1253 * <key>monitoring-supported</key> 1254 * <true/> 1255 * ... 1256 * 1257 * always false on Battery {capacity,charge}, Drive and Indicator types. 1258 * They cannot be monitored. 1259 * 1260 */ 1261 if ((edata->flags & ENVSYS_FMONNOTSUPP) || 1262 (edata->units == ENVSYS_INDICATOR) || 1263 (edata->units == ENVSYS_DRIVE) || 1264 (edata->units == ENVSYS_BATTERY_CAPACITY) || 1265 (edata->units == ENVSYS_BATTERY_CHARGE)) { 1266 if (sme_sensor_upbool(dict, "monitoring-supported", false)) 1267 goto out; 1268 } else { 1269 if (sme_sensor_upbool(dict, "monitoring-supported", true)) 1270 goto out; 1271 } 1272 1273 /* 1274 * Add the percentage boolean object, true if ENVSYS_FPERCENT 1275 * is set or false otherwise. 1276 * 1277 * ... 1278 * <key>want-percentage</key> 1279 * <true/> 1280 * ... 1281 */ 1282 if (edata->flags & ENVSYS_FPERCENT) 1283 if (sme_sensor_upbool(dict, "want-percentage", true)) 1284 goto out; 1285 1286 /* 1287 * Add the allow-rfact boolean object, true if 1288 * ENVSYS_FCHANGERFACT if set or false otherwise. 1289 * 1290 * ... 1291 * <key>allow-rfact</key> 1292 * <true/> 1293 * ... 1294 */ 1295 if (edata->units == ENVSYS_SVOLTS_DC || 1296 edata->units == ENVSYS_SVOLTS_AC) { 1297 if (edata->flags & ENVSYS_FCHANGERFACT) { 1298 if (sme_sensor_upbool(dict, "allow-rfact", true)) 1299 goto out; 1300 } else { 1301 if (sme_sensor_upbool(dict, "allow-rfact", false)) 1302 goto out; 1303 } 1304 } 1305 1306 /* 1307 * Add the object for battery capacity sensors: 1308 * 1309 * ... 1310 * <key>battery-capacity</key> 1311 * <string>NORMAL</string> 1312 * ... 1313 */ 1314 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1315 sdt = sme_get_description_table(SME_DESC_BATTERY_CAPACITY); 1316 for (j = 0; sdt[j].type != -1; j++) 1317 if (sdt[j].type == edata->value_cur) 1318 break; 1319 1320 if (sme_sensor_upstring(dict, "battery-capacity", sdt[j].desc)) 1321 goto out; 1322 } 1323 1324 /* 1325 * Add the drive-state object for drive sensors: 1326 * 1327 * ... 1328 * <key>drive-state</key> 1329 * <string>drive is online</string> 1330 * ... 1331 */ 1332 if (edata->units == ENVSYS_DRIVE) { 1333 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 1334 for (j = 0; sdt[j].type != -1; j++) 1335 if (sdt[j].type == edata->value_cur) 1336 break; 1337 1338 if (sme_sensor_upstring(dict, "drive-state", sdt[j].desc)) 1339 goto out; 1340 } 1341 1342 /* 1343 * Add the following objects if sensor is enabled... 1344 */ 1345 if (edata->state == ENVSYS_SVALID) { 1346 /* 1347 * Add the following objects: 1348 * 1349 * ... 1350 * <key>rpms</key> 1351 * <integer>2500</integer> 1352 * <key>rfact</key> 1353 * <integer>10000</integer> 1354 * <key>cur-value</key> 1355 * <integer>1250</integer> 1356 * <key>min-value</key> 1357 * <integer>800</integer> 1358 * <key>max-value</integer> 1359 * <integer>3000</integer> 1360 * <key>avg-value</integer> 1361 * <integer>1400</integer> 1362 * ... 1363 */ 1364 if (edata->units == ENVSYS_SFANRPM) 1365 if (sme_sensor_upuint32(dict, "rpms", edata->rpms)) 1366 goto out; 1367 1368 if (edata->units == ENVSYS_SVOLTS_AC || 1369 edata->units == ENVSYS_SVOLTS_DC) 1370 if (sme_sensor_upint32(dict, "rfact", edata->rfact)) 1371 goto out; 1372 1373 if (sme_sensor_upint32(dict, "cur-value", edata->value_cur)) 1374 goto out; 1375 1376 if (edata->flags & ENVSYS_FVALID_MIN) { 1377 if (sme_sensor_upint32(dict, 1378 "min-value", 1379 edata->value_min)) 1380 goto out; 1381 } 1382 1383 if (edata->flags & ENVSYS_FVALID_MAX) { 1384 if (sme_sensor_upint32(dict, 1385 "max-value", 1386 edata->value_max)) 1387 goto out; 1388 } 1389 1390 if (edata->flags & ENVSYS_FVALID_AVG) { 1391 if (sme_sensor_upint32(dict, 1392 "avg-value", 1393 edata->value_avg)) 1394 goto out; 1395 } 1396 } 1397 1398 /* 1399 * ... 1400 * </dict> 1401 * 1402 * Add the dictionary into the array. 1403 * 1404 */ 1405 if (!prop_array_add(array, dict)) { 1406 DPRINTF(("%s: prop_array_add\n", __func__)); 1407 goto bad; 1408 } 1409 1410 /* 1411 * Register a new event if a monitoring flag was set. 1412 */ 1413 if (edata->monitor) { 1414 sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP); 1415 sme_evdrv_t->sed_sdict = dict; 1416 sme_evdrv_t->sed_edata = edata; 1417 sme_evdrv_t->sed_sme = sme; 1418 sme_evdrv_t->sed_powertype = sdt_units[i].crittype; 1419 } 1420 1421 out: 1422 return sme_evdrv_t; 1423 1424 bad: 1425 prop_object_release(dict); 1426 return NULL; 1427 } 1428 1429 /* 1430 * Find the maximum of all currently reported values. 1431 * The provided callback decides wether a sensor is part of the 1432 * maximum calculation (by returning true) or ignored (callback 1433 * returns false). Example usage: callback selects temperature 1434 * sensors in a given thermal zone, the function calculates the 1435 * maximum currently reported temperature in this zone. 1436 * If the parameter "refresh" is true, new values will be aquired 1437 * from the hardware, if not, the last reported value will be used. 1438 */ 1439 uint32_t 1440 sysmon_envsys_get_max_value(bool (*predicate)(const envsys_data_t*), 1441 bool refresh) 1442 { 1443 struct sysmon_envsys *sme; 1444 uint32_t maxv, v; 1445 1446 maxv = 0; 1447 mutex_enter(&sme_global_mtx); 1448 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1449 sysmon_envsys_acquire(sme, false); 1450 v = sme_get_max_value(sme, predicate, refresh); 1451 sysmon_envsys_release(sme, false); 1452 if (v > maxv) 1453 maxv = v; 1454 } 1455 mutex_exit(&sme_global_mtx); 1456 return maxv; 1457 } 1458 1459 static uint32_t 1460 sme_get_max_value(struct sysmon_envsys *sme, 1461 bool (*predicate)(const envsys_data_t*), 1462 bool refresh) 1463 { 1464 envsys_data_t *edata; 1465 uint32_t maxv, v; 1466 1467 /* 1468 * Iterate over all sensors that match the predicate 1469 */ 1470 maxv = 0; 1471 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1472 if (!(*predicate)(edata)) 1473 continue; 1474 1475 /* 1476 * refresh sensor data via sme_refresh only if the 1477 * flag is not set. 1478 */ 1479 if (refresh && (sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 1480 mutex_enter(&sme->sme_mtx); 1481 (*sme->sme_refresh)(sme, edata); 1482 mutex_exit(&sme->sme_mtx); 1483 } 1484 1485 v = edata->value_cur; 1486 if (v > maxv) 1487 maxv = v; 1488 1489 } 1490 1491 return maxv; 1492 } 1493 1494 /* 1495 * sme_update_dictionary: 1496 * 1497 * + Update per-sensor dictionaries with new values if there were 1498 * changes, otherwise the object in dictionary is untouched. 1499 */ 1500 int 1501 sme_update_dictionary(struct sysmon_envsys *sme) 1502 { 1503 const struct sme_description_table *sdt; 1504 envsys_data_t *edata; 1505 prop_object_t array, dict, obj, obj2; 1506 int j, error = 0; 1507 1508 /* 1509 * Retrieve the array of dictionaries in device. 1510 */ 1511 array = prop_dictionary_get(sme_propd, sme->sme_name); 1512 if (prop_object_type(array) != PROP_TYPE_ARRAY) { 1513 DPRINTF(("%s: not an array (%s)\n", __func__, sme->sme_name)); 1514 return EINVAL; 1515 } 1516 1517 /* 1518 * Get the last dictionary on the array, this contains the 1519 * 'device-properties' sub-dictionary. 1520 */ 1521 obj = prop_array_get(array, prop_array_count(array) - 1); 1522 if (!obj || prop_object_type(obj) != PROP_TYPE_DICTIONARY) { 1523 DPRINTF(("%s: not a device-properties dictionary\n", __func__)); 1524 return EINVAL; 1525 } 1526 1527 obj2 = prop_dictionary_get(obj, "device-properties"); 1528 if (!obj2) 1529 return EINVAL; 1530 1531 /* 1532 * Update the 'refresh-timeout' property. 1533 */ 1534 if (!prop_dictionary_set_uint64(obj2, "refresh-timeout", 1535 sme->sme_events_timeout)) 1536 return EINVAL; 1537 1538 /* 1539 * - iterate over all sensors. 1540 * - fetch new data. 1541 * - check if data in dictionary is different than new data. 1542 * - update dictionary if there were changes. 1543 */ 1544 DPRINTF(("%s: updating '%s' with nsensors=%d\n", __func__, 1545 sme->sme_name, sme->sme_nsensors)); 1546 1547 /* 1548 * Don't bother with locking when traversing the queue, 1549 * the device is already marked as busy; if a sensor 1550 * is going to be removed or added it will have to wait. 1551 */ 1552 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1553 /* 1554 * refresh sensor data via sme_refresh only if the 1555 * flag is not set. 1556 */ 1557 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 1558 mutex_enter(&sme->sme_mtx); 1559 (*sme->sme_refresh)(sme, edata); 1560 mutex_exit(&sme->sme_mtx); 1561 } 1562 1563 /* 1564 * retrieve sensor's dictionary. 1565 */ 1566 dict = prop_array_get(array, edata->sensor); 1567 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) { 1568 DPRINTF(("%s: not a dictionary (%d:%s)\n", 1569 __func__, edata->sensor, sme->sme_name)); 1570 return EINVAL; 1571 } 1572 1573 /* 1574 * update sensor's state. 1575 */ 1576 sdt = sme_get_description_table(SME_DESC_STATES); 1577 for (j = 0; sdt[j].type != -1; j++) 1578 if (sdt[j].type == edata->state) 1579 break; 1580 1581 DPRINTFOBJ(("%s: sensor #%d type=%d (%s) flags=%d\n", 1582 __func__, edata->sensor, sdt[j].type, sdt[j].desc, 1583 edata->flags)); 1584 1585 error = sme_sensor_upstring(dict, "state", sdt[j].desc); 1586 if (error) 1587 break; 1588 1589 /* 1590 * update sensor's type. 1591 */ 1592 sdt = sme_get_description_table(SME_DESC_UNITS); 1593 for (j = 0; sdt[j].type != -1; j++) 1594 if (sdt[j].type == edata->units) 1595 break; 1596 1597 DPRINTFOBJ(("%s: sensor #%d units=%d (%s)\n", 1598 __func__, edata->sensor, sdt[j].type, sdt[j].desc)); 1599 1600 error = sme_sensor_upstring(dict, "type", sdt[j].desc); 1601 if (error) 1602 break; 1603 1604 /* 1605 * update sensor's current value. 1606 */ 1607 error = sme_sensor_upint32(dict, 1608 "cur-value", 1609 edata->value_cur); 1610 if (error) 1611 break; 1612 1613 /* 1614 * Battery charge, Integer and Indicator types do not 1615 * need the following objects, so skip them. 1616 */ 1617 if (edata->units == ENVSYS_INTEGER || 1618 edata->units == ENVSYS_INDICATOR || 1619 edata->units == ENVSYS_BATTERY_CHARGE) 1620 continue; 1621 1622 /* 1623 * update sensor flags. 1624 */ 1625 if (edata->flags & ENVSYS_FPERCENT) { 1626 error = sme_sensor_upbool(dict, 1627 "want-percentage", 1628 true); 1629 if (error) 1630 break; 1631 } 1632 1633 /* 1634 * update sensor's {avg,max,min}-value. 1635 */ 1636 if (edata->flags & ENVSYS_FVALID_MAX) { 1637 error = sme_sensor_upint32(dict, 1638 "max-value", 1639 edata->value_max); 1640 if (error) 1641 break; 1642 } 1643 1644 if (edata->flags & ENVSYS_FVALID_MIN) { 1645 error = sme_sensor_upint32(dict, 1646 "min-value", 1647 edata->value_min); 1648 if (error) 1649 break; 1650 } 1651 1652 if (edata->flags & ENVSYS_FVALID_AVG) { 1653 error = sme_sensor_upint32(dict, 1654 "avg-value", 1655 edata->value_avg); 1656 if (error) 1657 break; 1658 } 1659 1660 /* 1661 * update 'rpms' only for ENVSYS_SFANRPM sensors. 1662 */ 1663 if (edata->units == ENVSYS_SFANRPM) { 1664 error = sme_sensor_upuint32(dict, 1665 "rpms", 1666 edata->rpms); 1667 if (error) 1668 break; 1669 } 1670 1671 /* 1672 * update 'rfact' only for ENVSYS_SVOLTS_[AD]C sensors. 1673 */ 1674 if (edata->units == ENVSYS_SVOLTS_AC || 1675 edata->units == ENVSYS_SVOLTS_DC) { 1676 error = sme_sensor_upint32(dict, 1677 "rfact", 1678 edata->rfact); 1679 if (error) 1680 break; 1681 } 1682 1683 /* 1684 * update 'drive-state' only for ENVSYS_DRIVE sensors. 1685 */ 1686 if (edata->units == ENVSYS_DRIVE) { 1687 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 1688 for (j = 0; sdt[j].type != -1; j++) 1689 if (sdt[j].type == edata->value_cur) 1690 break; 1691 1692 error = sme_sensor_upstring(dict, 1693 "drive-state", 1694 sdt[j].desc); 1695 if (error) 1696 break; 1697 } 1698 1699 /* 1700 * update 'battery-capacity' only for ENVSYS_BATTERY_CAPACITY 1701 * sensors. 1702 */ 1703 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1704 sdt = 1705 sme_get_description_table(SME_DESC_BATTERY_CAPACITY); 1706 for (j = 0; sdt[j].type != -1; j++) 1707 if (sdt[j].type == edata->value_cur) 1708 break; 1709 1710 error = sme_sensor_upstring(dict, 1711 "battery-capacity", 1712 sdt[j].desc); 1713 if (error) 1714 break; 1715 } 1716 } 1717 1718 return error; 1719 } 1720 1721 /* 1722 * sme_userset_dictionary: 1723 * 1724 * + Parse the userland dictionary and run the appropiate tasks 1725 * that were specified. 1726 */ 1727 int 1728 sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict, 1729 prop_array_t array) 1730 { 1731 const struct sme_description_table *sdt; 1732 envsys_data_t *edata; 1733 prop_dictionary_t dict, tdict = NULL; 1734 prop_object_t obj, obj1, obj2, tobj = NULL; 1735 uint32_t props; 1736 uint64_t refresh_timo = 0; 1737 sysmon_envsys_lim_t lims; 1738 int i, error = 0; 1739 const char *blah; 1740 bool targetfound = false; 1741 1742 /* 1743 * The user wanted to change the refresh timeout value for this 1744 * device. 1745 * 1746 * Get the 'device-properties' object from the userland dictionary. 1747 */ 1748 obj = prop_dictionary_get(udict, "device-properties"); 1749 if (obj && prop_object_type(obj) == PROP_TYPE_DICTIONARY) { 1750 /* 1751 * Get the 'refresh-timeout' property for this device. 1752 */ 1753 obj1 = prop_dictionary_get(obj, "refresh-timeout"); 1754 if (obj1 && prop_object_type(obj1) == PROP_TYPE_NUMBER) { 1755 targetfound = true; 1756 refresh_timo = 1757 prop_number_unsigned_integer_value(obj1); 1758 if (refresh_timo < 1) 1759 error = EINVAL; 1760 else { 1761 mutex_enter(&sme->sme_mtx); 1762 sme->sme_events_timeout = refresh_timo; 1763 mutex_exit(&sme->sme_mtx); 1764 } 1765 } 1766 return error; 1767 1768 } else if (!obj) { 1769 /* 1770 * Get sensor's index from userland dictionary. 1771 */ 1772 obj = prop_dictionary_get(udict, "index"); 1773 if (!obj) 1774 return EINVAL; 1775 if (prop_object_type(obj) != PROP_TYPE_STRING) { 1776 DPRINTF(("%s: 'index' not a string\n", __func__)); 1777 return EINVAL; 1778 } 1779 } else 1780 return EINVAL; 1781 1782 /* 1783 * Don't bother with locking when traversing the queue, 1784 * the device is already marked as busy; if a sensor 1785 * is going to be removed or added it will have to wait. 1786 */ 1787 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1788 /* 1789 * Get a dictionary and check if it's our sensor by checking 1790 * at its index position. 1791 */ 1792 dict = prop_array_get(array, edata->sensor); 1793 obj1 = prop_dictionary_get(dict, "index"); 1794 1795 /* 1796 * is it our sensor? 1797 */ 1798 if (!prop_string_equals(obj1, obj)) 1799 continue; 1800 1801 props = 0; 1802 1803 /* 1804 * Check if a new description operation was 1805 * requested by the user and set new description. 1806 */ 1807 obj2 = prop_dictionary_get(udict, "description"); 1808 if (obj2 && prop_object_type(obj2) == PROP_TYPE_STRING) { 1809 targetfound = true; 1810 blah = prop_string_cstring_nocopy(obj2); 1811 1812 /* 1813 * Check for duplicate description. 1814 */ 1815 for (i = 0; i < sme->sme_nsensors; i++) { 1816 if (i == edata->sensor) 1817 continue; 1818 tdict = prop_array_get(array, i); 1819 tobj = 1820 prop_dictionary_get(tdict, "description"); 1821 if (prop_string_equals(obj2, tobj)) { 1822 error = EEXIST; 1823 goto out; 1824 } 1825 } 1826 1827 /* 1828 * Update the object in dictionary. 1829 */ 1830 mutex_enter(&sme->sme_mtx); 1831 error = sme_sensor_upstring(dict, 1832 "description", 1833 blah); 1834 if (error) { 1835 mutex_exit(&sme->sme_mtx); 1836 goto out; 1837 } 1838 1839 DPRINTF(("%s: sensor%d changed desc to: %s\n", 1840 __func__, edata->sensor, blah)); 1841 edata->upropset |= PROP_DESC; 1842 mutex_exit(&sme->sme_mtx); 1843 } 1844 1845 /* 1846 * did the user want to change the rfact? 1847 */ 1848 obj2 = prop_dictionary_get(udict, "rfact"); 1849 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1850 targetfound = true; 1851 if (edata->flags & ENVSYS_FCHANGERFACT) { 1852 mutex_enter(&sme->sme_mtx); 1853 edata->rfact = prop_number_integer_value(obj2); 1854 edata->upropset |= PROP_RFACT; 1855 mutex_exit(&sme->sme_mtx); 1856 DPRINTF(("%s: sensor%d changed rfact to %d\n", 1857 __func__, edata->sensor, edata->rfact)); 1858 } else { 1859 error = ENOTSUP; 1860 goto out; 1861 } 1862 } 1863 1864 sdt = sme_get_description_table(SME_DESC_UNITS); 1865 for (i = 0; sdt[i].type != -1; i++) 1866 if (sdt[i].type == edata->units) 1867 break; 1868 1869 /* 1870 * did the user want to set a critical capacity event? 1871 */ 1872 obj2 = prop_dictionary_get(udict, "critical-capacity"); 1873 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1874 targetfound = true; 1875 lims.sel_critmin = prop_number_integer_value(obj2); 1876 props |= PROP_BATTCAP; 1877 } 1878 1879 /* 1880 * did the user want to set a warning capacity event? 1881 */ 1882 obj2 = prop_dictionary_get(udict, "warning-capacity"); 1883 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1884 targetfound = true; 1885 lims.sel_warnmin = prop_number_integer_value(obj2); 1886 props |= PROP_BATTWARN; 1887 } 1888 1889 /* 1890 * did the user want to set a high capacity event? 1891 */ 1892 obj2 = prop_dictionary_get(udict, "high-capacity"); 1893 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1894 targetfound = true; 1895 lims.sel_warnmin = prop_number_integer_value(obj2); 1896 props |= PROP_BATTHIGH; 1897 } 1898 1899 /* 1900 * did the user want to set a maximum capacity event? 1901 */ 1902 obj2 = prop_dictionary_get(udict, "maximum-capacity"); 1903 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1904 targetfound = true; 1905 lims.sel_warnmin = prop_number_integer_value(obj2); 1906 props |= PROP_BATTMAX; 1907 } 1908 1909 /* 1910 * did the user want to set a critical max event? 1911 */ 1912 obj2 = prop_dictionary_get(udict, "critical-max"); 1913 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1914 targetfound = true; 1915 lims.sel_critmax = prop_number_integer_value(obj2); 1916 props |= PROP_CRITMAX; 1917 } 1918 1919 /* 1920 * did the user want to set a warning max event? 1921 */ 1922 obj2 = prop_dictionary_get(udict, "warning-max"); 1923 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1924 targetfound = true; 1925 lims.sel_warnmax = prop_number_integer_value(obj2); 1926 props |= PROP_WARNMAX; 1927 } 1928 1929 /* 1930 * did the user want to set a critical min event? 1931 */ 1932 obj2 = prop_dictionary_get(udict, "critical-min"); 1933 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1934 targetfound = true; 1935 lims.sel_critmin = prop_number_integer_value(obj2); 1936 props |= PROP_CRITMIN; 1937 } 1938 1939 /* 1940 * did the user want to set a warning min event? 1941 */ 1942 obj2 = prop_dictionary_get(udict, "warning-min"); 1943 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1944 targetfound = true; 1945 lims.sel_warnmin = prop_number_integer_value(obj2); 1946 props |= PROP_WARNMIN; 1947 } 1948 1949 if (props) { 1950 if (edata->flags & ENVSYS_FMONNOTSUPP) { 1951 error = ENOTSUP; 1952 goto out; 1953 } 1954 error = sme_event_register(dict, edata, sme, &lims, 1955 props, 1956 (edata->flags & ENVSYS_FPERCENT)? 1957 PENVSYS_EVENT_CAPACITY: 1958 PENVSYS_EVENT_LIMITS, 1959 sdt[i].crittype); 1960 if (error == EEXIST) 1961 error = 0; 1962 if (error) 1963 goto out; 1964 } 1965 1966 /* 1967 * All objects in dictionary were processed. 1968 */ 1969 break; 1970 } 1971 1972 out: 1973 /* 1974 * invalid target? return the error. 1975 */ 1976 if (!targetfound) 1977 error = EINVAL; 1978 1979 return error; 1980 } 1981