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