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