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