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