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