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