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