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