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