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