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