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