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