1 /* $NetBSD: sysmon_envsys.c,v 1.107 2010/04/11 01:12:28 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.107 2010/04/11 01:12:28 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_description_table *sdt_units; 524 envsys_data_t *oedata; 525 int i; 526 527 KASSERT(sme != NULL || edata != NULL); 528 529 /* 530 * Find the correct units for this sensor. 531 */ 532 sdt_units = sme_get_description_table(SME_DESC_UNITS); 533 for (i = 0; sdt_units[i].type != -1; i++) 534 if (sdt_units[i].type == edata->units) 535 break; 536 537 if (strcmp(sdt_units[i].desc, "unknown") == 0) 538 return EINVAL; 539 540 /* 541 * Check that description is not empty or duplicate. 542 */ 543 if (strlen(edata->desc) == 0) 544 return EINVAL; 545 546 mutex_enter(&sme->sme_mtx); 547 sysmon_envsys_acquire(sme, true); 548 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 549 if (strcmp(oedata->desc, edata->desc) == 0) { 550 sysmon_envsys_release(sme, true); 551 mutex_exit(&sme->sme_mtx); 552 return EEXIST; 553 } 554 } 555 /* 556 * Ok, the sensor has been added into the device queue. 557 */ 558 TAILQ_INSERT_TAIL(&sme->sme_sensors_list, edata, sensors_head); 559 560 /* 561 * Give the sensor a index position. 562 */ 563 edata->sensor = sme->sme_nsensors; 564 sme->sme_nsensors++; 565 sysmon_envsys_release(sme, true); 566 mutex_exit(&sme->sme_mtx); 567 568 DPRINTF(("%s: attached #%d (%s), units=%d (%s)\n", 569 __func__, edata->sensor, edata->desc, 570 sdt_units[i].type, sdt_units[i].desc)); 571 572 return 0; 573 } 574 575 /* 576 * sysmon_envsys_sensor_detach: 577 * 578 * + Detachs a sensor from a sysmon_envsys device and decrements the 579 * sensors count on success. 580 */ 581 int 582 sysmon_envsys_sensor_detach(struct sysmon_envsys *sme, envsys_data_t *edata) 583 { 584 envsys_data_t *oedata; 585 bool found = false; 586 587 KASSERT(sme != NULL || edata != NULL); 588 589 /* 590 * Check the sensor is already on the list. 591 */ 592 mutex_enter(&sme->sme_mtx); 593 sysmon_envsys_acquire(sme, true); 594 TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { 595 if (oedata->sensor == edata->sensor) { 596 found = true; 597 break; 598 } 599 } 600 601 if (!found) { 602 sysmon_envsys_release(sme, true); 603 mutex_exit(&sme->sme_mtx); 604 return EINVAL; 605 } 606 607 /* 608 * remove it and decrement the sensors count. 609 */ 610 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 611 sme->sme_nsensors--; 612 sysmon_envsys_release(sme, true); 613 mutex_exit(&sme->sme_mtx); 614 615 return 0; 616 } 617 618 619 /* 620 * sysmon_envsys_register: 621 * 622 * + Register a sysmon envsys device. 623 * + Create array of dictionaries for a device. 624 */ 625 int 626 sysmon_envsys_register(struct sysmon_envsys *sme) 627 { 628 struct sme_evdrv { 629 SLIST_ENTRY(sme_evdrv) evdrv_head; 630 sme_event_drv_t *evdrv; 631 }; 632 SLIST_HEAD(, sme_evdrv) sme_evdrv_list; 633 struct sme_evdrv *evdv = NULL; 634 struct sysmon_envsys *lsme; 635 prop_array_t array = NULL; 636 prop_dictionary_t dict, dict2; 637 envsys_data_t *edata = NULL; 638 sme_event_drv_t *this_evdrv; 639 int nevent; 640 int error = 0; 641 642 KASSERT(sme != NULL); 643 KASSERT(sme->sme_name != NULL); 644 645 /* 646 * Check if requested sysmon_envsys device is valid 647 * and does not exist already in the list. 648 */ 649 mutex_enter(&sme_global_mtx); 650 LIST_FOREACH(lsme, &sysmon_envsys_list, sme_list) { 651 if (strcmp(lsme->sme_name, sme->sme_name) == 0) { 652 mutex_exit(&sme_global_mtx); 653 return EEXIST; 654 } 655 } 656 mutex_exit(&sme_global_mtx); 657 658 /* 659 * sanity check: if SME_DISABLE_REFRESH is not set, 660 * the sme_refresh function callback must be non NULL. 661 */ 662 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 663 if (!sme->sme_refresh) 664 return EINVAL; 665 666 /* 667 * If the list of sensors is empty, there's no point to continue... 668 */ 669 if (TAILQ_EMPTY(&sme->sme_sensors_list)) { 670 DPRINTF(("%s: sensors list empty for %s\n", __func__, 671 sme->sme_name)); 672 return ENOTSUP; 673 } 674 675 /* 676 * Initialize the singly linked list for driver events. 677 */ 678 SLIST_INIT(&sme_evdrv_list); 679 680 array = prop_array_create(); 681 if (!array) 682 return ENOMEM; 683 684 /* 685 * Iterate over all sensors and create a dictionary per sensor. 686 * We must respect the order in which the sensors were added. 687 */ 688 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 689 dict = prop_dictionary_create(); 690 if (!dict) { 691 error = ENOMEM; 692 goto out2; 693 } 694 695 /* 696 * Create all objects in sensor's dictionary. 697 */ 698 this_evdrv = sme_add_sensor_dictionary(sme, array, 699 dict, edata); 700 if (this_evdrv) { 701 evdv = kmem_zalloc(sizeof(*evdv), KM_SLEEP); 702 evdv->evdrv = this_evdrv; 703 SLIST_INSERT_HEAD(&sme_evdrv_list, evdv, evdrv_head); 704 } 705 } 706 707 /* 708 * If the array does not contain any object (sensor), there's 709 * no need to attach the driver. 710 */ 711 if (prop_array_count(array) == 0) { 712 error = EINVAL; 713 DPRINTF(("%s: empty array for '%s'\n", __func__, 714 sme->sme_name)); 715 goto out; 716 } 717 718 /* 719 * Add the dictionary for the global properties of this device. 720 */ 721 dict2 = prop_dictionary_create(); 722 if (!dict2) { 723 error = ENOMEM; 724 goto out; 725 } 726 727 error = sme_add_property_dictionary(sme, array, dict2); 728 if (error) { 729 prop_object_release(dict2); 730 goto out; 731 } 732 733 /* 734 * Add the array into the global dictionary for the driver. 735 * 736 * <dict> 737 * <key>foo0</key> 738 * <array> 739 * ... 740 */ 741 mutex_enter(&sme_global_mtx); 742 if (!prop_dictionary_set(sme_propd, sme->sme_name, array)) { 743 error = EINVAL; 744 DPRINTF(("%s: prop_dictionary_set for '%s'\n", __func__, 745 sme->sme_name)); 746 goto out; 747 } 748 749 /* 750 * Add the device into the list. 751 */ 752 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); 753 sme->sme_fsensor = sysmon_envsys_next_sensor_index; 754 sysmon_envsys_next_sensor_index += sme->sme_nsensors; 755 mutex_exit(&sme_global_mtx); 756 757 out: 758 /* 759 * No errors? Make an initial data refresh if was requested, 760 * then register the events that were set in the driver. Do 761 * the refresh first in case it is needed to establish the 762 * limits or max_value needed by some events. 763 */ 764 if (error == 0) { 765 nevent = 0; 766 sysmon_task_queue_init(); 767 768 if (sme->sme_flags & SME_INIT_REFRESH) { 769 sysmon_task_queue_sched(0, sme_initial_refresh, sme); 770 DPRINTF(("%s: scheduled initial refresh for '%s'\n", 771 __func__, sme->sme_name)); 772 } 773 SLIST_FOREACH(evdv, &sme_evdrv_list, evdrv_head) { 774 sysmon_task_queue_sched(0, 775 sme_event_drvadd, evdv->evdrv); 776 nevent++; 777 } 778 DPRINTF(("%s: driver '%s' registered (nsens=%d nevent=%d)\n", 779 __func__, sme->sme_name, sme->sme_nsensors, nevent)); 780 } 781 782 out2: 783 while (!SLIST_EMPTY(&sme_evdrv_list)) { 784 evdv = SLIST_FIRST(&sme_evdrv_list); 785 SLIST_REMOVE_HEAD(&sme_evdrv_list, evdrv_head); 786 kmem_free(evdv, sizeof(*evdv)); 787 } 788 if (!error) 789 return 0; 790 791 /* 792 * Ugh... something wasn't right; unregister all events and sensors 793 * previously assigned and destroy the array with all its objects. 794 */ 795 DPRINTF(("%s: failed to register '%s' (%d)\n", __func__, 796 sme->sme_name, error)); 797 798 sme_event_unregister_all(sme); 799 while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { 800 edata = TAILQ_FIRST(&sme->sme_sensors_list); 801 TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); 802 } 803 sysmon_envsys_destroy_plist(array); 804 return error; 805 } 806 807 /* 808 * sysmon_envsys_destroy_plist: 809 * 810 * + Remove all objects from the array of dictionaries that is 811 * created in a sysmon envsys device. 812 */ 813 static void 814 sysmon_envsys_destroy_plist(prop_array_t array) 815 { 816 prop_object_iterator_t iter, iter2; 817 prop_dictionary_t dict; 818 prop_object_t obj; 819 820 KASSERT(array != NULL); 821 KASSERT(prop_object_type(array) == PROP_TYPE_ARRAY); 822 823 DPRINTFOBJ(("%s: objects in array=%d\n", __func__, 824 prop_array_count(array))); 825 826 iter = prop_array_iterator(array); 827 if (!iter) 828 return; 829 830 while ((dict = prop_object_iterator_next(iter))) { 831 KASSERT(prop_object_type(dict) == PROP_TYPE_DICTIONARY); 832 iter2 = prop_dictionary_iterator(dict); 833 if (!iter2) 834 goto out; 835 DPRINTFOBJ(("%s: iterating over dictionary\n", __func__)); 836 while ((obj = prop_object_iterator_next(iter2)) != NULL) { 837 DPRINTFOBJ(("%s: obj=%s\n", __func__, 838 prop_dictionary_keysym_cstring_nocopy(obj))); 839 prop_dictionary_remove(dict, 840 prop_dictionary_keysym_cstring_nocopy(obj)); 841 prop_object_iterator_reset(iter2); 842 } 843 prop_object_iterator_release(iter2); 844 DPRINTFOBJ(("%s: objects in dictionary:%d\n", 845 __func__, prop_dictionary_count(dict))); 846 prop_object_release(dict); 847 } 848 849 out: 850 prop_object_iterator_release(iter); 851 prop_object_release(array); 852 } 853 854 /* 855 * sysmon_envsys_unregister: 856 * 857 * + Unregister a sysmon envsys device. 858 */ 859 void 860 sysmon_envsys_unregister(struct sysmon_envsys *sme) 861 { 862 prop_array_t array; 863 struct sysmon_envsys *osme; 864 865 KASSERT(sme != NULL); 866 867 /* 868 * Unregister all events associated with device. 869 */ 870 sme_event_unregister_all(sme); 871 /* 872 * Decrement global sensors counter and the first_sensor index 873 * for remaining devices in the list (only used for compatibility 874 * with previous API), and remove the device from the list. 875 */ 876 mutex_enter(&sme_global_mtx); 877 sysmon_envsys_next_sensor_index -= sme->sme_nsensors; 878 LIST_FOREACH(osme, &sysmon_envsys_list, sme_list) { 879 if (osme->sme_fsensor >= sme->sme_fsensor) 880 osme->sme_fsensor -= sme->sme_nsensors; 881 } 882 LIST_REMOVE(sme, sme_list); 883 mutex_exit(&sme_global_mtx); 884 885 /* 886 * Remove the device (and all its objects) from the global dictionary. 887 */ 888 array = prop_dictionary_get(sme_propd, sme->sme_name); 889 if (array && prop_object_type(array) == PROP_TYPE_ARRAY) { 890 mutex_enter(&sme_global_mtx); 891 prop_dictionary_remove(sme_propd, sme->sme_name); 892 mutex_exit(&sme_global_mtx); 893 sysmon_envsys_destroy_plist(array); 894 } 895 /* 896 * And finally destroy the sysmon_envsys object. 897 */ 898 sysmon_envsys_destroy(sme); 899 } 900 901 /* 902 * sysmon_envsys_find: 903 * 904 * + Find a sysmon envsys device and mark it as busy 905 * once it's available. 906 */ 907 struct sysmon_envsys * 908 sysmon_envsys_find(const char *name) 909 { 910 struct sysmon_envsys *sme; 911 912 mutex_enter(&sme_global_mtx); 913 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 914 if (strcmp(sme->sme_name, name) == 0) { 915 sysmon_envsys_acquire(sme, false); 916 break; 917 } 918 } 919 mutex_exit(&sme_global_mtx); 920 921 return sme; 922 } 923 924 /* 925 * Compatibility function with the old API. 926 */ 927 struct sysmon_envsys * 928 sysmon_envsys_find_40(u_int idx) 929 { 930 struct sysmon_envsys *sme; 931 932 mutex_enter(&sme_global_mtx); 933 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 934 if (idx >= sme->sme_fsensor && 935 idx < (sme->sme_fsensor + sme->sme_nsensors)) { 936 sysmon_envsys_acquire(sme, false); 937 break; 938 } 939 } 940 mutex_exit(&sme_global_mtx); 941 942 return sme; 943 } 944 945 /* 946 * sysmon_envsys_acquire: 947 * 948 * + Wait until a sysmon envsys device is available and mark 949 * it as busy. 950 */ 951 void 952 sysmon_envsys_acquire(struct sysmon_envsys *sme, bool locked) 953 { 954 KASSERT(sme != NULL); 955 956 if (locked) { 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 } else { 961 mutex_enter(&sme->sme_mtx); 962 while (sme->sme_flags & SME_FLAG_BUSY) 963 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 964 sme->sme_flags |= SME_FLAG_BUSY; 965 mutex_exit(&sme->sme_mtx); 966 } 967 } 968 969 /* 970 * sysmon_envsys_release: 971 * 972 * + Unmark a sysmon envsys device as busy, and notify 973 * waiters. 974 */ 975 void 976 sysmon_envsys_release(struct sysmon_envsys *sme, bool locked) 977 { 978 KASSERT(sme != NULL); 979 980 if (locked) { 981 sme->sme_flags &= ~SME_FLAG_BUSY; 982 cv_broadcast(&sme->sme_condvar); 983 } else { 984 mutex_enter(&sme->sme_mtx); 985 sme->sme_flags &= ~SME_FLAG_BUSY; 986 cv_broadcast(&sme->sme_condvar); 987 mutex_exit(&sme->sme_mtx); 988 } 989 } 990 991 /* 992 * sme_initial_refresh: 993 * 994 * + Do an initial refresh of the sensors in a device just after 995 * interrupts are enabled in the autoconf(9) process. 996 * 997 */ 998 static void 999 sme_initial_refresh(void *arg) 1000 { 1001 struct sysmon_envsys *sme = arg; 1002 envsys_data_t *edata; 1003 1004 mutex_enter(&sme->sme_mtx); 1005 sysmon_envsys_acquire(sme, true); 1006 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) 1007 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) 1008 (*sme->sme_refresh)(sme, edata); 1009 sysmon_envsys_release(sme, true); 1010 mutex_exit(&sme->sme_mtx); 1011 } 1012 1013 /* 1014 * sme_sensor_dictionary_get: 1015 * 1016 * + Returns a dictionary of a device specified by its index 1017 * position. 1018 */ 1019 prop_dictionary_t 1020 sme_sensor_dictionary_get(prop_array_t array, const char *index) 1021 { 1022 prop_object_iterator_t iter; 1023 prop_dictionary_t dict; 1024 prop_object_t obj; 1025 1026 KASSERT(array != NULL || index != NULL); 1027 1028 iter = prop_array_iterator(array); 1029 if (!iter) 1030 return NULL; 1031 1032 while ((dict = prop_object_iterator_next(iter))) { 1033 obj = prop_dictionary_get(dict, "index"); 1034 if (prop_string_equals_cstring(obj, index)) 1035 break; 1036 } 1037 1038 prop_object_iterator_release(iter); 1039 return dict; 1040 } 1041 1042 /* 1043 * sme_remove_userprops: 1044 * 1045 * + Remove all properties from all devices that were set by 1046 * the ENVSYS_SETDICTIONARY ioctl. 1047 */ 1048 static void 1049 sme_remove_userprops(void) 1050 { 1051 struct sysmon_envsys *sme; 1052 prop_array_t array; 1053 prop_dictionary_t sdict; 1054 envsys_data_t *edata = NULL; 1055 char tmp[ENVSYS_DESCLEN]; 1056 sysmon_envsys_lim_t lims; 1057 int ptype; 1058 1059 mutex_enter(&sme_global_mtx); 1060 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1061 sysmon_envsys_acquire(sme, false); 1062 array = prop_dictionary_get(sme_propd, sme->sme_name); 1063 1064 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1065 (void)snprintf(tmp, sizeof(tmp), "sensor%d", 1066 edata->sensor); 1067 sdict = sme_sensor_dictionary_get(array, tmp); 1068 KASSERT(sdict != NULL); 1069 1070 ptype = 0; 1071 if (edata->upropset & PROP_BATTCAP) { 1072 prop_dictionary_remove(sdict, 1073 "critical-capacity"); 1074 ptype = PENVSYS_EVENT_CAPACITY; 1075 } 1076 1077 if (edata->upropset & PROP_BATTWARN) { 1078 prop_dictionary_remove(sdict, 1079 "warning-capacity"); 1080 ptype = PENVSYS_EVENT_CAPACITY; 1081 } 1082 1083 if (edata->upropset & PROP_BATTHIGH) { 1084 prop_dictionary_remove(sdict, 1085 "high-capacity"); 1086 ptype = PENVSYS_EVENT_CAPACITY; 1087 } 1088 1089 if (edata->upropset & PROP_BATTMAX) { 1090 prop_dictionary_remove(sdict, 1091 "maximum-capacity"); 1092 ptype = PENVSYS_EVENT_CAPACITY; 1093 } 1094 if (edata->upropset & PROP_WARNMAX) { 1095 prop_dictionary_remove(sdict, "warning-max"); 1096 ptype = PENVSYS_EVENT_LIMITS; 1097 } 1098 1099 if (edata->upropset & PROP_WARNMIN) { 1100 prop_dictionary_remove(sdict, "warning-min"); 1101 ptype = PENVSYS_EVENT_LIMITS; 1102 } 1103 1104 if (edata->upropset & PROP_CRITMAX) { 1105 prop_dictionary_remove(sdict, "critical-max"); 1106 ptype = PENVSYS_EVENT_LIMITS; 1107 } 1108 1109 if (edata->upropset & PROP_CRITMIN) { 1110 prop_dictionary_remove(sdict, "critical-min"); 1111 ptype = PENVSYS_EVENT_LIMITS; 1112 } 1113 if (edata->upropset & PROP_RFACT) { 1114 (void)sme_sensor_upint32(sdict, "rfact", 0); 1115 edata->rfact = 0; 1116 } 1117 1118 if (edata->upropset & PROP_DESC) 1119 (void)sme_sensor_upstring(sdict, 1120 "description", edata->desc); 1121 1122 if (ptype == 0) 1123 continue; 1124 1125 /* 1126 * If there were any limit values removed, we 1127 * need to revert to initial limits. 1128 * 1129 * First, tell the driver that we need it to 1130 * restore any h/w limits which may have been 1131 * changed to stored, boot-time values. Then 1132 * we need to retrieve those limits and update 1133 * the event data in the dictionary. 1134 */ 1135 if (sme->sme_set_limits) { 1136 DPRINTF(("%s: reset limits for %s %s\n", 1137 __func__, sme->sme_name, edata->desc)); 1138 (*sme->sme_set_limits)(sme, edata, NULL, NULL); 1139 } 1140 if (sme->sme_get_limits) { 1141 DPRINTF(("%s: retrieve limits for %s %s\n", 1142 __func__, sme->sme_name, edata->desc)); 1143 lims = edata->limits; 1144 (*sme->sme_get_limits)(sme, edata, &lims, 1145 &edata->upropset); 1146 } 1147 if (edata->upropset) { 1148 DPRINTF(("%s: install limits for %s %s\n", 1149 __func__, sme->sme_name, edata->desc)); 1150 sme_update_limits(sme, edata); 1151 } 1152 } 1153 1154 /* 1155 * Restore default timeout value. 1156 */ 1157 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1158 sysmon_envsys_release(sme, false); 1159 } 1160 mutex_exit(&sme_global_mtx); 1161 } 1162 1163 /* 1164 * sme_add_property_dictionary: 1165 * 1166 * + Add global properties into a device. 1167 */ 1168 static int 1169 sme_add_property_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1170 prop_dictionary_t dict) 1171 { 1172 prop_dictionary_t pdict; 1173 const char *class; 1174 int error = 0; 1175 1176 pdict = prop_dictionary_create(); 1177 if (!pdict) 1178 return EINVAL; 1179 1180 /* 1181 * Add the 'refresh-timeout' and 'dev-class' objects into the 1182 * 'device-properties' dictionary. 1183 * 1184 * ... 1185 * <dict> 1186 * <key>device-properties</key> 1187 * <dict> 1188 * <key>refresh-timeout</key> 1189 * <integer>120</integer< 1190 * <key>device-class</key> 1191 * <string>class_name</string> 1192 * </dict> 1193 * </dict> 1194 * ... 1195 * 1196 */ 1197 if (!sme->sme_events_timeout) 1198 sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; 1199 1200 if (!prop_dictionary_set_uint64(pdict, "refresh-timeout", 1201 sme->sme_events_timeout)) { 1202 error = EINVAL; 1203 goto out; 1204 } 1205 if (sme->sme_class == SME_CLASS_BATTERY) 1206 class = "battery"; 1207 else if (sme->sme_class == SME_CLASS_ACADAPTER) 1208 class = "ac-adapter"; 1209 else 1210 class = "other"; 1211 if (!prop_dictionary_set_cstring_nocopy(pdict, "device-class", class)) { 1212 error = EINVAL; 1213 goto out; 1214 } 1215 1216 if (!prop_dictionary_set(dict, "device-properties", pdict)) { 1217 error = EINVAL; 1218 goto out; 1219 } 1220 1221 /* 1222 * Add the device dictionary into the sysmon envsys array. 1223 */ 1224 if (!prop_array_add(array, dict)) 1225 error = EINVAL; 1226 1227 out: 1228 prop_object_release(pdict); 1229 return error; 1230 } 1231 1232 /* 1233 * sme_add_sensor_dictionary: 1234 * 1235 * + Adds the sensor objects into the dictionary and returns a pointer 1236 * to a sme_event_drv_t object if a monitoring flag was set 1237 * (or NULL otherwise). 1238 */ 1239 static sme_event_drv_t * 1240 sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array, 1241 prop_dictionary_t dict, envsys_data_t *edata) 1242 { 1243 const struct sme_description_table *sdt, *sdt_units; 1244 sme_event_drv_t *sme_evdrv_t = NULL; 1245 int i, j; 1246 char indexstr[ENVSYS_DESCLEN]; 1247 1248 /* 1249 * Find the correct units for this sensor. 1250 */ 1251 sdt_units = sme_get_description_table(SME_DESC_UNITS); 1252 for (i = 0; sdt_units[i].type != -1; i++) 1253 if (sdt_units[i].type == edata->units) 1254 break; 1255 1256 /* 1257 * Add the index sensor string. 1258 * 1259 * ... 1260 * <key>index</eyr 1261 * <string>sensor0</string> 1262 * ... 1263 */ 1264 (void)snprintf(indexstr, sizeof(indexstr), "sensor%d", edata->sensor); 1265 if (sme_sensor_upstring(dict, "index", indexstr)) 1266 goto bad; 1267 1268 /* 1269 * ... 1270 * <key>type</key> 1271 * <string>foo</string> 1272 * <key>description</key> 1273 * <string>blah blah</string> 1274 * ... 1275 */ 1276 if (sme_sensor_upstring(dict, "type", sdt_units[i].desc)) 1277 goto bad; 1278 1279 if (sme_sensor_upstring(dict, "description", edata->desc)) 1280 goto bad; 1281 1282 /* 1283 * Add sensor's state description. 1284 * 1285 * ... 1286 * <key>state</key> 1287 * <string>valid</string> 1288 * ... 1289 */ 1290 sdt = sme_get_description_table(SME_DESC_STATES); 1291 for (j = 0; sdt[j].type != -1; j++) 1292 if (sdt[j].type == edata->state) 1293 break; 1294 1295 DPRINTF(("%s: sensor desc=%s type=%d state=%d\n", 1296 __func__, edata->desc, edata->units, edata->state)); 1297 1298 if (sme_sensor_upstring(dict, "state", sdt[j].desc)) 1299 goto bad; 1300 1301 /* 1302 * Add the monitoring boolean object: 1303 * 1304 * ... 1305 * <key>monitoring-supported</key> 1306 * <true/> 1307 * ... 1308 * 1309 * always false on Battery {capacity,charge}, Drive and Indicator types. 1310 * They cannot be monitored. 1311 * 1312 */ 1313 if ((edata->flags & ENVSYS_FMONNOTSUPP) || 1314 (edata->units == ENVSYS_INDICATOR) || 1315 (edata->units == ENVSYS_DRIVE) || 1316 (edata->units == ENVSYS_BATTERY_CAPACITY) || 1317 (edata->units == ENVSYS_BATTERY_CHARGE)) { 1318 if (sme_sensor_upbool(dict, "monitoring-supported", false)) 1319 goto out; 1320 } else { 1321 if (sme_sensor_upbool(dict, "monitoring-supported", true)) 1322 goto out; 1323 } 1324 1325 /* 1326 * Add the percentage boolean object, true if ENVSYS_FPERCENT 1327 * is set or false otherwise. 1328 * 1329 * ... 1330 * <key>want-percentage</key> 1331 * <true/> 1332 * ... 1333 */ 1334 if (edata->flags & ENVSYS_FPERCENT) 1335 if (sme_sensor_upbool(dict, "want-percentage", true)) 1336 goto out; 1337 1338 /* 1339 * Add the allow-rfact boolean object, true if 1340 * ENVSYS_FCHANGERFACT if set or false otherwise. 1341 * 1342 * ... 1343 * <key>allow-rfact</key> 1344 * <true/> 1345 * ... 1346 */ 1347 if (edata->units == ENVSYS_SVOLTS_DC || 1348 edata->units == ENVSYS_SVOLTS_AC) { 1349 if (edata->flags & ENVSYS_FCHANGERFACT) { 1350 if (sme_sensor_upbool(dict, "allow-rfact", true)) 1351 goto out; 1352 } else { 1353 if (sme_sensor_upbool(dict, "allow-rfact", false)) 1354 goto out; 1355 } 1356 } 1357 1358 /* 1359 * Add the object for battery capacity sensors: 1360 * 1361 * ... 1362 * <key>battery-capacity</key> 1363 * <string>NORMAL</string> 1364 * ... 1365 */ 1366 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1367 sdt = sme_get_description_table(SME_DESC_BATTERY_CAPACITY); 1368 for (j = 0; sdt[j].type != -1; j++) 1369 if (sdt[j].type == edata->value_cur) 1370 break; 1371 1372 if (sme_sensor_upstring(dict, "battery-capacity", sdt[j].desc)) 1373 goto out; 1374 } 1375 1376 /* 1377 * Add the drive-state object for drive sensors: 1378 * 1379 * ... 1380 * <key>drive-state</key> 1381 * <string>drive is online</string> 1382 * ... 1383 */ 1384 if (edata->units == ENVSYS_DRIVE) { 1385 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 1386 for (j = 0; sdt[j].type != -1; j++) 1387 if (sdt[j].type == edata->value_cur) 1388 break; 1389 1390 if (sme_sensor_upstring(dict, "drive-state", sdt[j].desc)) 1391 goto out; 1392 } 1393 1394 /* 1395 * Add the following objects if sensor is enabled... 1396 */ 1397 if (edata->state == ENVSYS_SVALID) { 1398 /* 1399 * Add the following objects: 1400 * 1401 * ... 1402 * <key>rpms</key> 1403 * <integer>2500</integer> 1404 * <key>rfact</key> 1405 * <integer>10000</integer> 1406 * <key>cur-value</key> 1407 * <integer>1250</integer> 1408 * <key>min-value</key> 1409 * <integer>800</integer> 1410 * <key>max-value</integer> 1411 * <integer>3000</integer> 1412 * <key>avg-value</integer> 1413 * <integer>1400</integer> 1414 * ... 1415 */ 1416 if (edata->units == ENVSYS_SFANRPM) 1417 if (sme_sensor_upuint32(dict, "rpms", edata->rpms)) 1418 goto out; 1419 1420 if (edata->units == ENVSYS_SVOLTS_AC || 1421 edata->units == ENVSYS_SVOLTS_DC) 1422 if (sme_sensor_upint32(dict, "rfact", edata->rfact)) 1423 goto out; 1424 1425 if (sme_sensor_upint32(dict, "cur-value", edata->value_cur)) 1426 goto out; 1427 1428 if (edata->flags & ENVSYS_FVALID_MIN) { 1429 if (sme_sensor_upint32(dict, 1430 "min-value", 1431 edata->value_min)) 1432 goto out; 1433 } 1434 1435 if (edata->flags & ENVSYS_FVALID_MAX) { 1436 if (sme_sensor_upint32(dict, 1437 "max-value", 1438 edata->value_max)) 1439 goto out; 1440 } 1441 1442 if (edata->flags & ENVSYS_FVALID_AVG) { 1443 if (sme_sensor_upint32(dict, 1444 "avg-value", 1445 edata->value_avg)) 1446 goto out; 1447 } 1448 } 1449 1450 /* 1451 * ... 1452 * </dict> 1453 * 1454 * Add the dictionary into the array. 1455 * 1456 */ 1457 if (!prop_array_add(array, dict)) { 1458 DPRINTF(("%s: prop_array_add\n", __func__)); 1459 goto bad; 1460 } 1461 1462 /* 1463 * Register new event(s) if any monitoring flag was set. 1464 */ 1465 if (edata->flags & ENVSYS_FMONANY) { 1466 sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP); 1467 sme_evdrv_t->sed_sdict = dict; 1468 sme_evdrv_t->sed_edata = edata; 1469 sme_evdrv_t->sed_sme = sme; 1470 sme_evdrv_t->sed_powertype = sdt_units[i].crittype; 1471 } 1472 1473 out: 1474 return sme_evdrv_t; 1475 1476 bad: 1477 prop_object_release(dict); 1478 return NULL; 1479 } 1480 1481 /* 1482 * Find the maximum of all currently reported values. 1483 * The provided callback decides wether a sensor is part of the 1484 * maximum calculation (by returning true) or ignored (callback 1485 * returns false). Example usage: callback selects temperature 1486 * sensors in a given thermal zone, the function calculates the 1487 * maximum currently reported temperature in this zone. 1488 * If the parameter "refresh" is true, new values will be aquired 1489 * from the hardware, if not, the last reported value will be used. 1490 */ 1491 uint32_t 1492 sysmon_envsys_get_max_value(bool (*predicate)(const envsys_data_t*), 1493 bool refresh) 1494 { 1495 struct sysmon_envsys *sme; 1496 uint32_t maxv, v; 1497 1498 maxv = 0; 1499 mutex_enter(&sme_global_mtx); 1500 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 1501 sysmon_envsys_acquire(sme, false); 1502 v = sme_get_max_value(sme, predicate, refresh); 1503 sysmon_envsys_release(sme, false); 1504 if (v > maxv) 1505 maxv = v; 1506 } 1507 mutex_exit(&sme_global_mtx); 1508 return maxv; 1509 } 1510 1511 static uint32_t 1512 sme_get_max_value(struct sysmon_envsys *sme, 1513 bool (*predicate)(const envsys_data_t*), 1514 bool refresh) 1515 { 1516 envsys_data_t *edata; 1517 uint32_t maxv, v; 1518 1519 /* 1520 * Iterate over all sensors that match the predicate 1521 */ 1522 maxv = 0; 1523 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1524 if (!(*predicate)(edata)) 1525 continue; 1526 1527 /* 1528 * refresh sensor data via sme_refresh only if the 1529 * flag is not set. 1530 */ 1531 if (refresh && (sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 1532 mutex_enter(&sme->sme_mtx); 1533 (*sme->sme_refresh)(sme, edata); 1534 mutex_exit(&sme->sme_mtx); 1535 } 1536 1537 v = edata->value_cur; 1538 if (v > maxv) 1539 maxv = v; 1540 1541 } 1542 1543 return maxv; 1544 } 1545 1546 /* 1547 * sme_update_dictionary: 1548 * 1549 * + Update per-sensor dictionaries with new values if there were 1550 * changes, otherwise the object in dictionary is untouched. 1551 */ 1552 int 1553 sme_update_dictionary(struct sysmon_envsys *sme) 1554 { 1555 const struct sme_description_table *sdt; 1556 envsys_data_t *edata; 1557 prop_object_t array, dict, obj, obj2; 1558 int j, error = 0; 1559 1560 /* 1561 * Retrieve the array of dictionaries in device. 1562 */ 1563 array = prop_dictionary_get(sme_propd, sme->sme_name); 1564 if (prop_object_type(array) != PROP_TYPE_ARRAY) { 1565 DPRINTF(("%s: not an array (%s)\n", __func__, sme->sme_name)); 1566 return EINVAL; 1567 } 1568 1569 /* 1570 * Get the last dictionary on the array, this contains the 1571 * 'device-properties' sub-dictionary. 1572 */ 1573 obj = prop_array_get(array, prop_array_count(array) - 1); 1574 if (!obj || prop_object_type(obj) != PROP_TYPE_DICTIONARY) { 1575 DPRINTF(("%s: not a device-properties dictionary\n", __func__)); 1576 return EINVAL; 1577 } 1578 1579 obj2 = prop_dictionary_get(obj, "device-properties"); 1580 if (!obj2) 1581 return EINVAL; 1582 1583 /* 1584 * Update the 'refresh-timeout' property. 1585 */ 1586 if (!prop_dictionary_set_uint64(obj2, "refresh-timeout", 1587 sme->sme_events_timeout)) 1588 return EINVAL; 1589 1590 /* 1591 * - iterate over all sensors. 1592 * - fetch new data. 1593 * - check if data in dictionary is different than new data. 1594 * - update dictionary if there were changes. 1595 */ 1596 DPRINTF(("%s: updating '%s' with nsensors=%d\n", __func__, 1597 sme->sme_name, sme->sme_nsensors)); 1598 1599 /* 1600 * Don't bother with locking when traversing the queue, 1601 * the device is already marked as busy; if a sensor 1602 * is going to be removed or added it will have to wait. 1603 */ 1604 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 1605 /* 1606 * refresh sensor data via sme_refresh only if the 1607 * flag is not set. 1608 */ 1609 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 1610 mutex_enter(&sme->sme_mtx); 1611 (*sme->sme_refresh)(sme, edata); 1612 mutex_exit(&sme->sme_mtx); 1613 } 1614 1615 /* 1616 * retrieve sensor's dictionary. 1617 */ 1618 dict = prop_array_get(array, edata->sensor); 1619 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) { 1620 DPRINTF(("%s: not a dictionary (%d:%s)\n", 1621 __func__, edata->sensor, sme->sme_name)); 1622 return EINVAL; 1623 } 1624 1625 /* 1626 * update sensor's state. 1627 */ 1628 sdt = sme_get_description_table(SME_DESC_STATES); 1629 for (j = 0; sdt[j].type != -1; j++) 1630 if (sdt[j].type == edata->state) 1631 break; 1632 1633 DPRINTFOBJ(("%s: sensor #%d type=%d (%s) flags=%d\n", 1634 __func__, edata->sensor, sdt[j].type, sdt[j].desc, 1635 edata->flags)); 1636 1637 error = sme_sensor_upstring(dict, "state", sdt[j].desc); 1638 if (error) 1639 break; 1640 1641 /* 1642 * update sensor's type. 1643 */ 1644 sdt = sme_get_description_table(SME_DESC_UNITS); 1645 for (j = 0; sdt[j].type != -1; j++) 1646 if (sdt[j].type == edata->units) 1647 break; 1648 1649 DPRINTFOBJ(("%s: sensor #%d units=%d (%s)\n", 1650 __func__, edata->sensor, sdt[j].type, sdt[j].desc)); 1651 1652 error = sme_sensor_upstring(dict, "type", sdt[j].desc); 1653 if (error) 1654 break; 1655 1656 /* 1657 * update sensor's current value. 1658 */ 1659 error = sme_sensor_upint32(dict, 1660 "cur-value", 1661 edata->value_cur); 1662 if (error) 1663 break; 1664 1665 /* 1666 * Battery charge, Integer and Indicator types do not 1667 * need the following objects, so skip them. 1668 */ 1669 if (edata->units == ENVSYS_INTEGER || 1670 edata->units == ENVSYS_INDICATOR || 1671 edata->units == ENVSYS_BATTERY_CHARGE) 1672 continue; 1673 1674 /* 1675 * update sensor flags. 1676 */ 1677 if (edata->flags & ENVSYS_FPERCENT) { 1678 error = sme_sensor_upbool(dict, 1679 "want-percentage", 1680 true); 1681 if (error) 1682 break; 1683 } 1684 1685 /* 1686 * update sensor's {avg,max,min}-value. 1687 */ 1688 if (edata->flags & ENVSYS_FVALID_MAX) { 1689 error = sme_sensor_upint32(dict, 1690 "max-value", 1691 edata->value_max); 1692 if (error) 1693 break; 1694 } 1695 1696 if (edata->flags & ENVSYS_FVALID_MIN) { 1697 error = sme_sensor_upint32(dict, 1698 "min-value", 1699 edata->value_min); 1700 if (error) 1701 break; 1702 } 1703 1704 if (edata->flags & ENVSYS_FVALID_AVG) { 1705 error = sme_sensor_upint32(dict, 1706 "avg-value", 1707 edata->value_avg); 1708 if (error) 1709 break; 1710 } 1711 1712 /* 1713 * update 'rpms' only for ENVSYS_SFANRPM sensors. 1714 */ 1715 if (edata->units == ENVSYS_SFANRPM) { 1716 error = sme_sensor_upuint32(dict, 1717 "rpms", 1718 edata->rpms); 1719 if (error) 1720 break; 1721 } 1722 1723 /* 1724 * update 'rfact' only for ENVSYS_SVOLTS_[AD]C sensors. 1725 */ 1726 if (edata->units == ENVSYS_SVOLTS_AC || 1727 edata->units == ENVSYS_SVOLTS_DC) { 1728 error = sme_sensor_upint32(dict, 1729 "rfact", 1730 edata->rfact); 1731 if (error) 1732 break; 1733 } 1734 1735 /* 1736 * update 'drive-state' only for ENVSYS_DRIVE sensors. 1737 */ 1738 if (edata->units == ENVSYS_DRIVE) { 1739 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 1740 for (j = 0; sdt[j].type != -1; j++) 1741 if (sdt[j].type == edata->value_cur) 1742 break; 1743 1744 error = sme_sensor_upstring(dict, 1745 "drive-state", 1746 sdt[j].desc); 1747 if (error) 1748 break; 1749 } 1750 1751 /* 1752 * update 'battery-capacity' only for ENVSYS_BATTERY_CAPACITY 1753 * sensors. 1754 */ 1755 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 1756 sdt = 1757 sme_get_description_table(SME_DESC_BATTERY_CAPACITY); 1758 for (j = 0; sdt[j].type != -1; j++) 1759 if (sdt[j].type == edata->value_cur) 1760 break; 1761 1762 error = sme_sensor_upstring(dict, 1763 "battery-capacity", 1764 sdt[j].desc); 1765 if (error) 1766 break; 1767 } 1768 } 1769 1770 return error; 1771 } 1772 1773 /* 1774 * sme_userset_dictionary: 1775 * 1776 * + Parse the userland dictionary and run the appropiate tasks 1777 * that were specified. 1778 */ 1779 int 1780 sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict, 1781 prop_array_t array) 1782 { 1783 const struct sme_description_table *sdt; 1784 envsys_data_t *edata; 1785 prop_dictionary_t dict, tdict = NULL; 1786 prop_object_t obj, obj1, obj2, tobj = NULL; 1787 uint32_t props; 1788 uint64_t refresh_timo = 0; 1789 sysmon_envsys_lim_t lims; 1790 int i, error = 0; 1791 const char *blah; 1792 bool targetfound = false; 1793 1794 /* 1795 * The user wanted to change the refresh timeout value for this 1796 * device. 1797 * 1798 * Get the 'device-properties' object from the userland dictionary. 1799 */ 1800 obj = prop_dictionary_get(udict, "device-properties"); 1801 if (obj && prop_object_type(obj) == PROP_TYPE_DICTIONARY) { 1802 /* 1803 * Get the 'refresh-timeout' property for this device. 1804 */ 1805 obj1 = prop_dictionary_get(obj, "refresh-timeout"); 1806 if (obj1 && prop_object_type(obj1) == PROP_TYPE_NUMBER) { 1807 targetfound = true; 1808 refresh_timo = 1809 prop_number_unsigned_integer_value(obj1); 1810 if (refresh_timo < 1) 1811 error = EINVAL; 1812 else { 1813 mutex_enter(&sme->sme_mtx); 1814 sme->sme_events_timeout = refresh_timo; 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_get_description_table(SME_DESC_UNITS); 1917 for (i = 0; sdt[i].type != -1; i++) 1918 if (sdt[i].type == edata->units) 1919 break; 1920 1921 /* 1922 * did the user want to set a critical capacity event? 1923 */ 1924 obj2 = prop_dictionary_get(udict, "critical-capacity"); 1925 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1926 targetfound = true; 1927 lims.sel_critmin = prop_number_integer_value(obj2); 1928 props |= PROP_BATTCAP; 1929 } 1930 1931 /* 1932 * did the user want to set a warning capacity event? 1933 */ 1934 obj2 = prop_dictionary_get(udict, "warning-capacity"); 1935 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1936 targetfound = true; 1937 lims.sel_warnmin = prop_number_integer_value(obj2); 1938 props |= PROP_BATTWARN; 1939 } 1940 1941 /* 1942 * did the user want to set a high capacity event? 1943 */ 1944 obj2 = prop_dictionary_get(udict, "high-capacity"); 1945 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1946 targetfound = true; 1947 lims.sel_warnmin = prop_number_integer_value(obj2); 1948 props |= PROP_BATTHIGH; 1949 } 1950 1951 /* 1952 * did the user want to set a maximum capacity event? 1953 */ 1954 obj2 = prop_dictionary_get(udict, "maximum-capacity"); 1955 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1956 targetfound = true; 1957 lims.sel_warnmin = prop_number_integer_value(obj2); 1958 props |= PROP_BATTMAX; 1959 } 1960 1961 /* 1962 * did the user want to set a critical max event? 1963 */ 1964 obj2 = prop_dictionary_get(udict, "critical-max"); 1965 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1966 targetfound = true; 1967 lims.sel_critmax = prop_number_integer_value(obj2); 1968 props |= PROP_CRITMAX; 1969 } 1970 1971 /* 1972 * did the user want to set a warning max event? 1973 */ 1974 obj2 = prop_dictionary_get(udict, "warning-max"); 1975 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1976 targetfound = true; 1977 lims.sel_warnmax = prop_number_integer_value(obj2); 1978 props |= PROP_WARNMAX; 1979 } 1980 1981 /* 1982 * did the user want to set a critical min event? 1983 */ 1984 obj2 = prop_dictionary_get(udict, "critical-min"); 1985 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1986 targetfound = true; 1987 lims.sel_critmin = prop_number_integer_value(obj2); 1988 props |= PROP_CRITMIN; 1989 } 1990 1991 /* 1992 * did the user want to set a warning min event? 1993 */ 1994 obj2 = prop_dictionary_get(udict, "warning-min"); 1995 if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { 1996 targetfound = true; 1997 lims.sel_warnmin = prop_number_integer_value(obj2); 1998 props |= PROP_WARNMIN; 1999 } 2000 2001 if (props) { 2002 if (edata->flags & ENVSYS_FMONNOTSUPP) { 2003 error = ENOTSUP; 2004 goto out; 2005 } 2006 error = sme_event_register(dict, edata, sme, &lims, 2007 props, 2008 (edata->flags & ENVSYS_FPERCENT)? 2009 PENVSYS_EVENT_CAPACITY: 2010 PENVSYS_EVENT_LIMITS, 2011 sdt[i].crittype); 2012 if (error == EEXIST) 2013 error = 0; 2014 if (error) 2015 goto out; 2016 } 2017 2018 /* 2019 * All objects in dictionary were processed. 2020 */ 2021 break; 2022 } 2023 2024 out: 2025 /* 2026 * invalid target? return the error. 2027 */ 2028 if (!targetfound) 2029 error = EINVAL; 2030 2031 return error; 2032 } 2033 2034 /* 2035 * + sysmon_envsys_foreach_sensor 2036 * 2037 * Walk through the devices' sensor lists and execute the callback. 2038 * If the callback returns false, the remainder of the current 2039 * device's sensors are skipped. 2040 */ 2041 void 2042 sysmon_envsys_foreach_sensor(sysmon_envsys_callback_t func, void *arg, 2043 bool refresh) 2044 { 2045 struct sysmon_envsys *sme; 2046 envsys_data_t *sensor; 2047 2048 mutex_enter(&sme_global_mtx); 2049 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 2050 2051 sysmon_envsys_acquire(sme, false); 2052 TAILQ_FOREACH(sensor, &sme->sme_sensors_list, sensors_head) { 2053 if (refresh && 2054 (sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 2055 mutex_enter(&sme->sme_mtx); 2056 (*sme->sme_refresh)(sme, sensor); 2057 mutex_exit(&sme->sme_mtx); 2058 } 2059 if (!(*func)(sme, sensor, arg)) 2060 break; 2061 } 2062 sysmon_envsys_release(sme, false); 2063 } 2064 mutex_exit(&sme_global_mtx); 2065 } 2066