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