sysmon_envsys.c revision 1.58 1 /* $NetBSD: sysmon_envsys.c,v 1.58 2007/09/08 03:17:38 xtraeme Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Juan Romero Pardines.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Juan Romero Pardines
21 * for the NetBSD Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*-
40 * Copyright (c) 2000 Zembu Labs, Inc.
41 * All rights reserved.
42 *
43 * Author: Jason R. Thorpe <thorpej (at) zembu.com>
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions
47 * are met:
48 * 1. Redistributions of source code must retain the above copyright
49 * notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 * notice, this list of conditions and the following disclaimer in the
52 * documentation and/or other materials provided with the distribution.
53 * 3. All advertising materials mentioning features or use of this software
54 * must display the following acknowledgement:
55 * This product includes software developed by Zembu Labs, Inc.
56 * 4. Neither the name of Zembu Labs nor the names of its employees may
57 * be used to endorse or promote products derived from this software
58 * without specific prior written permission.
59 *
60 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS
61 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR-
62 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
63 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
64 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
65 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
66 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
67 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
68 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
69 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
70 */
71
72 /*
73 * Environmental sensor framework for sysmon, exported to userland
74 * with proplib(3).
75 */
76
77 #include <sys/cdefs.h>
78 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.58 2007/09/08 03:17:38 xtraeme Exp $");
79
80 #include <sys/param.h>
81 #include <sys/types.h>
82 #include <sys/conf.h>
83 #include <sys/errno.h>
84 #include <sys/fcntl.h>
85 #include <sys/kernel.h>
86 #include <sys/systm.h>
87 #include <sys/proc.h>
88 #include <sys/mutex.h>
89 #include <sys/kmem.h>
90
91 #include <dev/sysmon/sysmonvar.h>
92 #include <dev/sysmon/sysmon_envsysvar.h>
93 #include <dev/sysmon/sysmon_taskq.h>
94
95 static prop_dictionary_t sme_propd;
96 static kcondvar_t sme_list_cv;
97
98 kmutex_t sme_mtx, sme_event_init_mtx;
99 kcondvar_t sme_event_cv;
100
101 static uint32_t sysmon_envsys_next_sensor_index = 0;
102 static struct sysmon_envsys *sysmon_envsys_find_40(u_int);
103
104 static void sysmon_envsys_release(struct sysmon_envsys *);
105 static void sysmon_envsys_destroy_plist(prop_array_t);
106 static int sme_register_sensorname(struct sysmon_envsys *, envsys_data_t *);
107
108 /*
109 * sysmon_envsys_init:
110 *
111 * + Initialize global mutexes, dictionary and the linked lists.
112 */
113 void
114 sysmon_envsys_init(void)
115 {
116 LIST_INIT(&sysmon_envsys_list);
117 LIST_INIT(&sme_events_list);
118 mutex_init(&sme_mtx, MUTEX_DRIVER, IPL_NONE);
119 mutex_init(&sme_event_init_mtx, MUTEX_DRIVER, IPL_NONE);
120 cv_init(&sme_list_cv, "smefind");
121 cv_init(&sme_event_cv, "smeevent");
122 sme_propd = prop_dictionary_create();
123 }
124
125 /*
126 * sysmonopen_envsys:
127 *
128 * + Open the system monitor device.
129 */
130 int
131 sysmonopen_envsys(dev_t dev, int flag, int mode, struct lwp *l)
132 {
133 return 0;
134 }
135
136 /*
137 * sysmonclose_envsys:
138 *
139 * + Close the system monitor device.
140 */
141 int
142 sysmonclose_envsys(dev_t dev, int flag, int mode, struct lwp *l)
143 {
144 /* Nothing to do */
145 return 0;
146 }
147
148 /*
149 * sysmonioctl_envsys:
150 *
151 * + Perform a sysmon envsys control request.
152 */
153 int
154 sysmonioctl_envsys(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
155 {
156 struct sysmon_envsys *sme = NULL;
157 int error = 0;
158 u_int oidx;
159
160 switch (cmd) {
161 case ENVSYS_GETDICTIONARY:
162 {
163 struct plistref *plist = (struct plistref *)data;
164
165 /*
166 * Update all sysmon envsys devices dictionaries with
167 * new data if it's different than we have currently
168 * in the dictionary.
169 */
170 mutex_enter(&sme_mtx);
171 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
172 sme->sme_flags |= SME_FLAG_BUSY;
173 error = sme_update_dictionary(sme);
174 if (error) {
175 DPRINTF(("%s: sme_update_dictionary, "
176 "error=%d\n", __func__, error));
177 sme->sme_flags &= ~SME_FLAG_BUSY;
178 mutex_exit(&sme_mtx);
179 return error;
180 }
181 sme->sme_flags &= ~SME_FLAG_BUSY;
182 }
183 mutex_exit(&sme_mtx);
184 /*
185 * Copy global dictionary to userland.
186 */
187 error = prop_dictionary_copyout_ioctl(plist, cmd, sme_propd);
188 break;
189 }
190 case ENVSYS_SETDICTIONARY:
191 {
192 const struct plistref *plist = (const struct plistref *)data;
193 prop_dictionary_t udict;
194 prop_object_t obj;
195 const char *devname = NULL;
196
197 if ((flag & FWRITE) == 0)
198 return EPERM;
199
200 /*
201 * Get dictionary from userland.
202 */
203 error = prop_dictionary_copyin_ioctl(plist, cmd, &udict);
204 if (error) {
205 DPRINTF(("%s: copyin_ioctl error=%d\n",
206 __func__, error));
207 break;
208 }
209
210 /*
211 * Parse "driver-name" key to obtain the driver we
212 * are searching for.
213 */
214 obj = prop_dictionary_get(udict, "driver-name");
215 if (obj == NULL || prop_object_type(obj) != PROP_TYPE_STRING) {
216 DPRINTF(("%s: driver-name failed\n", __func__));
217 prop_object_release(udict);
218 error = EINVAL;
219 break;
220 }
221
222 /* driver name */
223 devname = prop_string_cstring_nocopy(obj);
224
225 /* find the correct sme device */
226 sme = sysmon_envsys_find(devname);
227 if (sme == NULL) {
228 DPRINTF(("%s: NULL sme\n", __func__));
229 prop_object_release(udict);
230 error = EINVAL;
231 break;
232 }
233
234 /*
235 * Find the correct array object with the string supplied
236 * by the userland dictionary.
237 */
238 obj = prop_dictionary_get(sme_propd, devname);
239 if (prop_object_type(obj) != PROP_TYPE_ARRAY) {
240 DPRINTF(("%s: array device failed\n", __func__));
241 sysmon_envsys_release(sme);
242 prop_object_release(udict);
243 error = EINVAL;
244 break;
245 }
246
247 /* do the real work now */
248 error = sme_userset_dictionary(sme, udict, obj);
249 sysmon_envsys_release(sme);
250 prop_object_release(udict);
251 break;
252 }
253 /*
254 * Compatibility functions with the old interface, only
255 * implemented ENVSYS_GTREDATA and ENVSYS_GTREINFO; enough
256 * to make old applications work.
257 */
258 case ENVSYS_GTREDATA:
259 {
260 struct envsys_tre_data *tred = (void *)data;
261 envsys_data_t *edata = NULL;
262
263 tred->validflags = 0;
264
265 sme = sysmon_envsys_find_40(tred->sensor);
266 if (sme == NULL)
267 break;
268
269 mutex_enter(&sme_mtx);
270 oidx = tred->sensor;
271 tred->sensor = SME_SENSOR_IDX(sme, tred->sensor);
272
273 DPRINTFOBJ(("%s: sensor=%d oidx=%d dev=%s nsensors=%d\n",
274 __func__, tred->sensor, oidx, sme->sme_name,
275 sme->sme_nsensors));
276
277 edata = &sme->sme_sensor_data[tred->sensor];
278
279 if (tred->sensor < sme->sme_nsensors) {
280 if ((sme->sme_flags & SME_DISABLE_GTREDATA) == 0) {
281 error = (*sme->sme_gtredata)(sme, edata);
282 if (error) {
283 DPRINTF(("%s: sme_gtredata failed\n",
284 __func__));
285 mutex_exit(&sme_mtx);
286 return error;
287 }
288 }
289
290 /* copy required values to the old interface */
291 tred->sensor = edata->sensor;
292 tred->cur.data_us = edata->value_cur;
293 tred->cur.data_s = edata->value_cur;
294 tred->max.data_us = edata->value_max;
295 tred->max.data_s = edata->value_max;
296 tred->min.data_us = edata->value_min;
297 tred->min.data_s = edata->value_min;
298 tred->avg.data_us = edata->value_avg;
299 tred->avg.data_s = edata->value_avg;
300 tred->units = edata->units;
301
302 tred->validflags |= ENVSYS_FVALID;
303 tred->validflags |= ENVSYS_FCURVALID;
304
305 if (edata->flags & ENVSYS_FPERCENT) {
306 tred->validflags |= ENVSYS_FMAXVALID;
307 tred->validflags |= ENVSYS_FFRACVALID;
308 }
309
310 if (edata->state == ENVSYS_SINVALID ||
311 edata->flags & ENVSYS_FNOTVALID) {
312 tred->validflags &= ~ENVSYS_FCURVALID;
313 tred->cur.data_us = tred->cur.data_s = 0;
314 }
315
316 DPRINTFOBJ(("%s: sensor=%s tred->cur.data_s=%d\n",
317 __func__, edata->desc, tred->cur.data_s));
318 DPRINTFOBJ(("%s: tred->validflags=%d tred->units=%d"
319 " tred->sensor=%d\n", __func__, tred->validflags,
320 tred->units, tred->sensor));
321 }
322 tred->sensor = oidx;
323 mutex_exit(&sme_mtx);
324
325 break;
326 }
327 case ENVSYS_GTREINFO:
328 {
329 struct envsys_basic_info *binfo = (void *)data;
330 envsys_data_t *edata = NULL;
331
332 binfo->validflags = 0;
333
334 sme = sysmon_envsys_find_40(binfo->sensor);
335 if (sme == NULL)
336 break;
337
338 mutex_enter(&sme_mtx);
339 oidx = binfo->sensor;
340 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor);
341
342 edata = &sme->sme_sensor_data[binfo->sensor];
343
344 binfo->validflags |= ENVSYS_FVALID;
345
346 if (binfo->sensor < sme->sme_nsensors) {
347 binfo->units = edata->units;
348 (void)strlcpy(binfo->desc, edata->desc,
349 sizeof(binfo->desc));
350 }
351
352 DPRINTFOBJ(("%s: binfo->units=%d binfo->validflags=%d\n",
353 __func__, binfo->units, binfo->validflags));
354 DPRINTFOBJ(("%s: binfo->desc=%s binfo->sensor=%d\n",
355 __func__, binfo->desc, binfo->sensor));
356
357 binfo->sensor = oidx;
358 mutex_exit(&sme_mtx);
359
360 break;
361 }
362 default:
363 error = ENOTTY;
364 break;
365 }
366
367 return error;
368 }
369
370 /*
371 * sysmon_envsys_register:
372 *
373 * + Register a sysmon envsys device.
374 * + Create array of dictionaries for a device.
375 */
376 int
377 sysmon_envsys_register(struct sysmon_envsys *sme)
378 {
379 struct sme_evdrv {
380 SLIST_ENTRY(sme_evdrv) evdrv_head;
381 sme_event_drv_t *evdrv;
382 };
383 SLIST_HEAD(, sme_evdrv) sme_evdrv_list;
384 struct sme_evdrv *sme_evdrv = NULL;
385 struct sysmon_envsys *lsme;
386 prop_dictionary_t dict;
387 prop_array_t array;
388 envsys_data_t *edata = NULL;
389 int i, error = 0;
390
391 KASSERT(sme != NULL);
392 KASSERT(sme->sme_name != NULL);
393 KASSERT(sme->sme_sensor_data != NULL);
394
395 /*
396 * sme_nsensors is mandatory...
397 */
398 if (!sme->sme_nsensors)
399 return EINVAL;
400
401 /*
402 * sanity check: if SME_DISABLE_GTREDATA is not set,
403 * the sme_gtredata function callback must be non NULL.
404 */
405 if ((sme->sme_flags & SME_DISABLE_GTREDATA) == 0) {
406 if (sme->sme_gtredata == NULL)
407 return EINVAL;
408 }
409
410
411 /* create the sysmon envsys device array. */
412 array = prop_array_create();
413 if (array == NULL)
414 return ENOMEM;
415
416 /*
417 * Initialize the singly linked list for sensor descriptions.
418 */
419 SLIST_INIT(&sme->sme_names_list);
420
421 /*
422 * Initialize the singly linked list for driver events.
423 */
424 SLIST_INIT(&sme_evdrv_list);
425 /*
426 * Iterate over all sensors and create a dictionary per sensor,
427 * checking firstly if sensor description is unique.
428 */
429 for (i = 0; i < sme->sme_nsensors; i++) {
430 edata = &sme->sme_sensor_data[i];
431 /*
432 * Check if sensor description is unique.
433 */
434 if (sme_register_sensorname(sme, edata))
435 continue;
436
437 dict = prop_dictionary_create();
438 if (dict == NULL) {
439 error = ENOMEM;
440 goto out2;
441 }
442
443 /*
444 * Create all objects in sensor's dictionary.
445 */
446 sme_evdrv = kmem_zalloc(sizeof(*sme_evdrv), KM_SLEEP);
447 sme_evdrv->evdrv = sme_add_sensor_dictionary(sme,
448 array, dict, edata);
449 if (sme_evdrv->evdrv)
450 SLIST_INSERT_HEAD(&sme_evdrv_list,
451 sme_evdrv, evdrv_head);
452 }
453
454 /*
455 * Check if requested sysmon_envsys device is valid
456 * and does not exist already in the list.
457 */
458 mutex_enter(&sme_mtx);
459 sme->sme_flags |= SME_FLAG_BUSY;
460 LIST_FOREACH(lsme, &sysmon_envsys_list, sme_list) {
461 if (strcmp(lsme->sme_name, sme->sme_name) == 0) {
462 error = EEXIST;
463 goto out;
464 }
465 }
466
467 /*
468 * If the array does not contain any object (sensor), there's
469 * no need to attach the driver.
470 */
471 if (prop_array_count(array) == 0) {
472 error = EINVAL;
473 DPRINTF(("%s: empty array for '%s'\n", __func__,
474 sme->sme_name));
475 goto out;
476 }
477 /*
478 * Add the array into the global dictionary for the driver.
479 *
480 * <dict>
481 * <key>foo0</key>
482 * <array>
483 * ...
484 */
485 if (!prop_dictionary_set(sme_propd, sme->sme_name, array)) {
486 error = EINVAL;
487 DPRINTF(("%s: prop_dictionary_set for '%s'\n", __func__,
488 sme->sme_name));
489 goto out;
490 }
491 /*
492 * Add the device into the list.
493 */
494 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list);
495 sme->sme_fsensor = sysmon_envsys_next_sensor_index;
496 sysmon_envsys_next_sensor_index += sme->sme_nsensors;
497 out:
498 sme->sme_uniqsensors = 0;
499 mutex_exit(&sme_mtx);
500
501 if (error == 0) {
502 i = 0;
503 SLIST_FOREACH(sme_evdrv, &sme_evdrv_list, evdrv_head) {
504 if (i == 0)
505 sysmon_task_queue_init();
506 sysmon_task_queue_sched(0,
507 sme_event_drvadd, sme_evdrv->evdrv);
508 }
509 DPRINTF(("%s: driver '%s' registered (nsens=%d)\n",
510 __func__, sme->sme_name, sme->sme_nsensors));
511 return 0;
512 }
513 out2:
514 DPRINTF(("%s: failed to register '%s' (%d)\n", __func__,
515 sme->sme_name, error));
516 while (!SLIST_EMPTY(&sme_evdrv_list)) {
517 sme_evdrv = SLIST_FIRST(&sme_evdrv_list);
518 SLIST_REMOVE_HEAD(&sme_evdrv_list, evdrv_head);
519 kmem_free(sme_evdrv, sizeof(*sme_evdrv));
520 }
521 sme_event_unregister_all(sme->sme_name);
522 sysmon_envsys_destroy_plist(array);
523 return error;
524 }
525
526 /*
527 * sysmon_envsys_destroy_plist:
528 *
529 * + Remove all objects from the array of dictionaries that is
530 * created in a sysmon envsys device.
531 */
532 static void
533 sysmon_envsys_destroy_plist(prop_array_t array)
534 {
535 prop_dictionary_t dict;
536 prop_object_iterator_t iter, iter2;
537 prop_object_t obj;
538
539 KASSERT(array != NULL);
540
541 iter = prop_array_iterator(array);
542 if (iter == NULL)
543 return;
544
545 while ((dict = prop_object_iterator_next(iter)) != NULL) {
546 iter2 = prop_dictionary_iterator(dict);
547 if (iter2) {
548 while ((obj = prop_object_iterator_next(iter2)) != NULL)
549 prop_object_release(obj);
550
551 prop_object_iterator_release(iter2);
552 }
553 prop_object_release(dict);
554 }
555
556 prop_object_iterator_release(iter);
557 }
558
559 /*
560 * sysmon_envsys_unregister:
561 *
562 * + Unregister a sysmon envsys device.
563 */
564 void
565 sysmon_envsys_unregister(struct sysmon_envsys *sme)
566 {
567 struct sme_sensor_names *snames;
568 prop_array_t array;
569
570 KASSERT(sme != NULL);
571
572 mutex_enter(&sme_mtx);
573 while (sme->sme_flags & SME_FLAG_BUSY) {
574 sme->sme_flags |= SME_FLAG_WANTED;
575 cv_wait(&sme_list_cv, &sme_mtx);
576 }
577 sysmon_envsys_next_sensor_index -= sme->sme_nsensors;
578 /*
579 * Remove all sensor descriptions from the singly linked list.
580 */
581 while (!SLIST_EMPTY(&sme->sme_names_list)) {
582 snames = SLIST_FIRST(&sme->sme_names_list);
583 SLIST_REMOVE_HEAD(&sme->sme_names_list, sme_names);
584 kmem_free(snames, sizeof(*snames));
585 }
586 /*
587 * Unregister all events associated with this device.
588 */
589 sme_event_unregister_all(sme->sme_name);
590 LIST_REMOVE(sme, sme_list);
591 mutex_exit(&sme_mtx);
592 /*
593 * Remove the device (and all its objects) from the global dictionary.
594 */
595 array = prop_dictionary_get(sme_propd, sme->sme_name);
596 if (array) {
597 sysmon_envsys_destroy_plist(array);
598 prop_dictionary_remove(sme_propd, sme->sme_name);
599 }
600 }
601
602 /*
603 * sysmon_envsys_find:
604 *
605 * + Find a sysmon envsys device.
606 */
607 struct sysmon_envsys *
608 sysmon_envsys_find(const char *name)
609 {
610 struct sysmon_envsys *sme;
611
612 mutex_enter(&sme_mtx);
613 again:
614 for (sme = LIST_FIRST(&sysmon_envsys_list); sme != NULL;
615 sme = LIST_NEXT(sme, sme_list)) {
616 if (strcmp(sme->sme_name, name) == 0) {
617 if (sme->sme_flags & SME_FLAG_BUSY) {
618 sme->sme_flags |= SME_FLAG_WANTED;
619 cv_wait(&sme_list_cv, &sme_mtx);
620 goto again;
621 }
622 sme->sme_flags |= SME_FLAG_BUSY;
623 break;
624 }
625 }
626 mutex_exit(&sme_mtx);
627 return sme;
628 }
629
630 /*
631 * sysmon_envsys_release:
632 *
633 * + Release a sysmon envsys device.
634 */
635 void
636 sysmon_envsys_release(struct sysmon_envsys *sme)
637 {
638 mutex_enter(&sme_mtx);
639 if (sme->sme_flags & SME_FLAG_WANTED)
640 cv_broadcast(&sme_list_cv);
641 sme->sme_flags &= ~(SME_FLAG_BUSY | SME_FLAG_WANTED);
642 mutex_exit(&sme_mtx);
643 }
644
645 /* compatibility function */
646 struct sysmon_envsys *
647 sysmon_envsys_find_40(u_int idx)
648 {
649 struct sysmon_envsys *sme;
650
651 mutex_enter(&sme_mtx);
652 for (sme = LIST_FIRST(&sysmon_envsys_list); sme != NULL;
653 sme = LIST_NEXT(sme, sme_list)) {
654 if (idx >= sme->sme_fsensor &&
655 idx < (sme->sme_fsensor + sme->sme_nsensors))
656 break;
657 }
658 mutex_exit(&sme_mtx);
659 return sme;
660 }
661
662 /*
663 * sme_register_sensorname:
664 *
665 * + Register a sensor description into the list maintained per device.
666 */
667 static int
668 sme_register_sensorname(struct sysmon_envsys *sme, envsys_data_t *edata)
669 {
670 struct sme_sensor_names *snames, *snames2 = NULL;
671
672 KASSERT(edata != NULL);
673
674 SLIST_FOREACH(snames2, &sme->sme_names_list, sme_names) {
675 /*
676 * Match sensors with empty and duplicate description.
677 */
678 if (strlen(edata->desc) == 0 ||
679 strcmp(snames2->desc, edata->desc) == 0)
680 if (snames2->assigned) {
681 edata->flags |= ENVSYS_FNOTVALID;
682 DPRINTF(("%s: wrong sensor name='%s'\n",
683 sme->sme_name, edata->desc));
684 return EEXIST;
685 }
686 }
687
688 snames = kmem_zalloc(sizeof(*snames), KM_NOSLEEP);
689 if (snames == NULL)
690 return ENOMEM;
691
692 snames->assigned = true;
693 (void)strlcpy(snames->desc, edata->desc, sizeof(snames->desc));
694 DPRINTF(("%s: registering sensor name='%s'\n",
695 sme->sme_name, edata->desc));
696 SLIST_INSERT_HEAD(&sme->sme_names_list, snames, sme_names);
697 sme->sme_uniqsensors++;
698
699 return 0;
700 }
701
702 /*
703 * sme_add_sensor_dictionary:
704 *
705 * + Add the objects into the dictionary.
706 */
707 sme_event_drv_t *
708 sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array,
709 prop_dictionary_t dict, envsys_data_t *edata)
710 {
711 const struct sme_description_table *sdt, *sdt_units;
712 sme_event_drv_t *sme_evdrv_t = NULL;
713 int i, j;
714
715 i = j = 0;
716
717 /* find the correct unit for this sensor. */
718 sdt_units = sme_get_description_table(SME_DESC_UNITS);
719 for (i = 0; sdt_units[i].type != -1; i++)
720 if (sdt_units[i].type == edata->units)
721 break;
722
723 if (strcmp(sdt_units[i].desc, "unknown") == 0) {
724 DPRINTF(("%s: invalid units type for sensor=%d\n",
725 __func__, edata->sensor));
726 goto invalidate_sensor;
727 }
728
729 /*
730 * ...
731 * <key>type</key>
732 * <string>foo</string>
733 * <key>description</key>
734 * <string>blah blah</string>
735 * ...
736 */
737 if (sme_sensor_upstring(dict, "type", sdt_units[i].desc))
738 goto invalidate_sensor;
739
740 if (strlen(edata->desc) == 0) {
741 DPRINTF(("%s: invalid description for sensor=%d\n",
742 __func__, edata->sensor));
743 goto invalidate_sensor;
744 }
745
746 if (sme_sensor_upstring(dict, "description", edata->desc))
747 goto invalidate_sensor;
748
749 /*
750 * Add sensor's state description.
751 *
752 * ...
753 * <key>state</key>
754 * <string>valid</string>
755 * ...
756 */
757 sdt = sme_get_description_table(SME_DESC_STATES);
758 for (j = 0; sdt[j].type != -1; j++)
759 if (sdt[j].type == edata->state)
760 break;
761
762 if (strcmp(sdt[j].desc, "unknown") == 0) {
763 DPRINTF(("%s: invalid state for sensor=%d\n",
764 __func__, edata->sensor));
765 goto invalidate_sensor;
766 }
767
768 DPRINTF(("%s: sensor desc=%s type=%d state=%d\n",
769 __func__, edata->desc, edata->units, edata->state));
770
771 if (sme_sensor_upstring(dict, "state", sdt[j].desc))
772 goto invalidate_sensor;
773
774 /*
775 * Add the monitoring boolean object:
776 *
777 * ...
778 * <key>monitoring-supported</key>
779 * <true/>
780 * ...
781 *
782 * always false on Battery state, Drive and Indicator types.
783 * They cannot be monitored.
784 *
785 */
786 if ((edata->flags & ENVSYS_FMONNOTSUPP) ||
787 (edata->units == ENVSYS_INDICATOR) ||
788 (edata->units == ENVSYS_DRIVE) ||
789 (edata->units == ENVSYS_BATTERY_STATE)) {
790 if (sme_sensor_upbool(dict, "monitoring-supported", false))
791 goto out;
792 } else {
793 if (sme_sensor_upbool(dict, "monitoring-supported", true))
794 goto out;
795 }
796
797 /*
798 * add the percentage boolean object:
799 *
800 * ...
801 * <key>want-percentage</key>
802 * <true/>
803 * ...
804 */
805 if (edata->flags & ENVSYS_FPERCENT)
806 if (sme_sensor_upbool(dict, "want-percentage", true))
807 goto out;
808
809 /*
810 * Add the battery-state object for battery state sensors:
811 *
812 * ...
813 * <key>battery-state</key>
814 * <string>NORMAL</string>
815 * ...
816 */
817 if (edata->units == ENVSYS_BATTERY_STATE) {
818 sdt = sme_get_description_table(SME_DESC_BATTERY_STATES);
819 for (j = 0; sdt[j].type != -1; j++)
820 if (sdt[j].type == edata->value_cur)
821 break;
822
823 if (sme_sensor_upstring(dict, "battery-state", sdt[j].desc))
824 goto out;
825 }
826
827 /*
828 * Add the drive-state object for drive sensors:
829 *
830 * ...
831 * <key>drive-state</key>
832 * <string>drive is online</string>
833 * ...
834 */
835 if (edata->units == ENVSYS_DRIVE) {
836 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
837 for (j = 0; sdt[j].type != -1; j++)
838 if (sdt[j].type == edata->value_cur)
839 break;
840
841 if (sme_sensor_upstring(dict, "drive-state", sdt[j].desc))
842 goto out;
843 }
844
845 /*
846 * if sensor is enabled, add the following properties...
847 */
848 if (edata->state == ENVSYS_SVALID) {
849 /*
850 * ...
851 * <key>rpms</key>
852 * <integer>2500</integer>
853 * <key>rfact</key>
854 * <integer>10000</integer>
855 * <key>cur-value</key>
856 * <integer>1250</integer>
857 * <key>min-value</key>
858 * <integer>800</integer>
859 * <key>max-value</integer>
860 * <integer>3000</integer>
861 * <key>avg-value</integer>
862 * <integer>1400</integer>
863 * </dict>
864 */
865 if (edata->units == ENVSYS_SFANRPM)
866 if (sme_sensor_upuint32(dict, "rpms", edata->rpms))
867 goto out;
868
869 if (edata->units == ENVSYS_SVOLTS_AC ||
870 edata->units == ENVSYS_SVOLTS_DC)
871 if (sme_sensor_upint32(dict, "rfact", edata->rfact))
872 goto out;
873
874 if (sme_sensor_upint32(dict, "cur-value", edata->value_cur))
875 goto out;
876
877 if (edata->flags & ENVSYS_FVALID_MIN) {
878 if (sme_sensor_upint32(dict,
879 "min-value",
880 edata->value_min))
881 goto out;
882 }
883
884 if (edata->flags & ENVSYS_FVALID_MAX) {
885 if (sme_sensor_upint32(dict,
886 "max-value",
887 edata->value_max))
888 goto out;
889 }
890
891 if (edata->flags & ENVSYS_FVALID_AVG) {
892 if (sme_sensor_upint32(dict,
893 "avg-value",
894 edata->value_avg))
895 goto out;
896 }
897 }
898
899 /*
900 * ...
901 * </array>
902 *
903 * Add the dictionary into the array.
904 *
905 */
906
907 if (!prop_array_set(array, sme->sme_uniqsensors - 1, dict)) {
908 DPRINTF(("%s: prop_array_add\n", __func__));
909 goto invalidate_sensor;
910 }
911
912 /*
913 * Add a new event if a monitoring flag was set.
914 */
915 if (edata->monitor) {
916 sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP);
917 sme_evdrv_t->sdict = dict;
918 sme_evdrv_t->edata = edata;
919 sme_evdrv_t->sme = sme;
920 sme_evdrv_t->powertype = sdt_units[i].crittype;
921 }
922
923 out:
924 return sme_evdrv_t;
925
926 invalidate_sensor:
927 edata->flags |= ENVSYS_FNOTVALID;
928 return sme_evdrv_t;
929 }
930
931 /*
932 * sme_update_dictionary:
933 *
934 * + Update per-sensor dictionaries with new values if there were
935 * changes, otherwise the object in dictionary is untouched.
936 */
937 int
938 sme_update_dictionary(struct sysmon_envsys *sme)
939 {
940 const struct sme_description_table *sdt;
941 envsys_data_t *edata;
942 prop_object_t array, dict;
943 int i, j, error, invalid;
944
945 KASSERT(mutex_owned(&sme_mtx));
946
947 error = invalid = 0;
948 array = dict = NULL;
949
950 /* retrieve the array of dictionaries in device. */
951 array = prop_dictionary_get(sme_propd, sme->sme_name);
952 if (prop_object_type(array) != PROP_TYPE_ARRAY) {
953 DPRINTF(("%s: not an array (%s)\n", __func__, sme->sme_name));
954 return EINVAL;
955 }
956
957 /*
958 * - iterate over all sensors.
959 * - fetch new data.
960 * - check if data in dictionary is different than new data.
961 * - update dictionary if there were changes.
962 */
963 DPRINTF(("%s: updating '%s' with nsensors=%d\n", __func__,
964 sme->sme_name, sme->sme_nsensors));
965
966 for (i = 0; i < sme->sme_nsensors; i++) {
967 edata = &sme->sme_sensor_data[i];
968 /* skip invalid sensors */
969 if (edata->flags & ENVSYS_FNOTVALID) {
970 DPRINTF(("%s: invalid sensor=%s idx=%d\n",
971 __func__, edata->desc, edata->sensor));
972 invalid++;
973 continue;
974 }
975
976 /*
977 * refresh sensor data via sme_gtredata only if the
978 * flag is not set.
979 */
980 if ((sme->sme_flags & SME_DISABLE_GTREDATA) == 0) {
981 error = (*sme->sme_gtredata)(sme, edata);
982 if (error) {
983 DPRINTF(("%s: gtredata[%d] failed\n",
984 __func__, i));
985 return error;
986 }
987 }
988
989 /* retrieve sensor's dictionary. */
990 dict = prop_array_get(array, i - invalid);
991 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) {
992 DPRINTF(("%s: not a dictionary (%d:%s)\n",
993 __func__, edata->sensor, sme->sme_name));
994 return EINVAL;
995 }
996
997 /* update sensor's state */
998 sdt = sme_get_description_table(SME_DESC_STATES);
999 for (j = 0; sdt[j].type != -1; j++)
1000 if (sdt[j].type == edata->state)
1001 break;
1002
1003 DPRINTFOBJ(("%s: state=%s type=%d flags=%d "
1004 "units=%d sensor=%d\n", __func__, sdt[j].desc,
1005 sdt[j].type, edata->flags, edata->units, edata->sensor));
1006
1007 /* update sensor state */
1008 error = sme_sensor_upstring(dict, "state", sdt[j].desc);
1009 if (error)
1010 break;
1011
1012 /* update sensor type */
1013 sdt = sme_get_description_table(SME_DESC_UNITS);
1014 for (j = 0; sdt[j].type != -1; j++)
1015 if (sdt[j].type == edata->units)
1016 break;
1017
1018 error = sme_sensor_upstring(dict, "type", sdt[j].desc);
1019 if (error)
1020 break;
1021
1022 /* update sensor current value */
1023 error = sme_sensor_upint32(dict,
1024 "cur-value",
1025 edata->value_cur);
1026 if (error)
1027 break;
1028
1029 /*
1030 * Integer and Indicator types do not the following
1031 * values, so skip them.
1032 */
1033 if (edata->units == ENVSYS_INTEGER ||
1034 edata->units == ENVSYS_INDICATOR)
1035 continue;
1036
1037 /* update sensor flags */
1038 if (edata->flags & ENVSYS_FPERCENT) {
1039 error = sme_sensor_upbool(dict,
1040 "want-percentage",
1041 true);
1042 if (error)
1043 break;
1044 }
1045
1046 if (edata->flags & ENVSYS_FVALID_MAX) {
1047 error = sme_sensor_upint32(dict,
1048 "max-value",
1049 edata->value_max);
1050 if (error)
1051 break;
1052 }
1053
1054 if (edata->flags & ENVSYS_FVALID_MIN) {
1055 error = sme_sensor_upint32(dict,
1056 "min-value",
1057 edata->value_min);
1058 if (error)
1059 break;
1060 }
1061
1062 if (edata->flags & ENVSYS_FVALID_AVG) {
1063 error = sme_sensor_upint32(dict,
1064 "avg-value",
1065 edata->value_avg);
1066 if (error)
1067 break;
1068 }
1069
1070 /* update 'rpms' only in ENVSYS_SFANRPM. */
1071 if (edata->units == ENVSYS_SFANRPM) {
1072 error = sme_sensor_upuint32(dict,
1073 "rpms",
1074 edata->rpms);
1075 if (error)
1076 break;
1077 }
1078
1079 /* update 'rfact' only in ENVSYS_SVOLTS_[AD]C. */
1080 if (edata->units == ENVSYS_SVOLTS_AC ||
1081 edata->units == ENVSYS_SVOLTS_DC) {
1082 error = sme_sensor_upint32(dict,
1083 "rfact",
1084 edata->rfact);
1085 if (error)
1086 break;
1087 }
1088
1089 /* update 'drive-state' only in ENVSYS_DRIVE. */
1090 if (edata->units == ENVSYS_DRIVE) {
1091 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
1092 for (j = 0; sdt[j].type != -1; j++)
1093 if (sdt[j].type == edata->value_cur)
1094 break;
1095
1096 error = sme_sensor_upstring(dict,
1097 "drive-state",
1098 sdt[j].desc);
1099 if (error)
1100 break;
1101 }
1102
1103 /* update 'battery-state' only in ENVSYS_BATTERY_STATE. */
1104 if (edata->units == ENVSYS_BATTERY_STATE) {
1105 sdt =
1106 sme_get_description_table(SME_DESC_BATTERY_STATES);
1107 for (j = 0; sdt[j].type != -1; j++)
1108 if (sdt[j].type == edata->value_cur)
1109 break;
1110
1111 error = sme_sensor_upstring(dict,
1112 "battery-state",
1113 sdt[j].desc);
1114 if (error)
1115 break;
1116 }
1117 }
1118
1119 return error;
1120 }
1121
1122 /*
1123 * sme_userset_dictionary:
1124 *
1125 * + Parse the userland dictionary and run the appropiate
1126 * task that was requested.
1127 */
1128 int
1129 sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict,
1130 prop_array_t array)
1131 {
1132 const struct sme_description_table *sdt;
1133 envsys_data_t *edata, *nedata;
1134 prop_dictionary_t dict;
1135 prop_object_t obj, obj1, obj2;
1136 int32_t critval;
1137 int i, invalid, error;
1138 const char *blah, *sname;
1139 bool targetfound = false;
1140
1141 error = invalid = 0;
1142 blah = sname = NULL;
1143
1144 /* get sensor's name from userland dictionary. */
1145 obj = prop_dictionary_get(udict, "sensor-name");
1146 if (prop_object_type(obj) != PROP_TYPE_STRING) {
1147 DPRINTF(("%s: sensor-name failed\n", __func__));
1148 return EINVAL;
1149 }
1150
1151 /* iterate over the sensors to find the right one */
1152 for (i = 0; i < sme->sme_nsensors; i++) {
1153 edata = &sme->sme_sensor_data[i];
1154 /* skip sensors with duplicate description */
1155 if (edata->flags & ENVSYS_FNOTVALID) {
1156 invalid++;
1157 continue;
1158 }
1159
1160 dict = prop_array_get(array, i - invalid);
1161 obj1 = prop_dictionary_get(dict, "description");
1162
1163 /* is it our sensor? */
1164 if (!prop_string_equals(obj1, obj))
1165 continue;
1166
1167 /*
1168 * Check if a new description operation was
1169 * requested by the user and set new description.
1170 */
1171 if ((obj2 = prop_dictionary_get(udict, "new-description"))) {
1172 targetfound = true;
1173 blah = prop_string_cstring_nocopy(obj2);
1174
1175 for (i = 0; i < sme->sme_nsensors; i++) {
1176 if (i == edata->sensor)
1177 continue;
1178
1179 nedata = &sme->sme_sensor_data[i];
1180 if (strcmp(blah, nedata->desc) == 0) {
1181 error = EEXIST;
1182 break;
1183 }
1184 }
1185
1186 if (error)
1187 break;
1188
1189 error = sme_sensor_upstring(dict,
1190 "description",
1191 blah);
1192 if (!error)
1193 (void)strlcpy(edata->desc,
1194 blah,
1195 sizeof(edata->desc));
1196
1197 break;
1198 }
1199
1200 /* did the user want to remove a critical capacity limit? */
1201 obj2 = prop_dictionary_get(udict, "remove-critical-cap");
1202 if (obj2 != NULL) {
1203 targetfound = true;
1204 if ((edata->flags & ENVSYS_FMONNOTSUPP) ||
1205 (edata->flags & ENVSYS_FPERCENT) == 0) {
1206 error = ENOTSUP;
1207 break;
1208 }
1209
1210 sname = prop_string_cstring_nocopy(obj);
1211 error = sme_event_unregister(sname,
1212 PENVSYS_EVENT_BATT_USERCAP);
1213 if (error)
1214 break;
1215
1216 prop_dictionary_remove(dict, "critical-capacity");
1217 break;
1218 }
1219
1220 /* did the user want to remove a critical min limit? */
1221 obj2 = prop_dictionary_get(udict, "remove-cmin-limit");
1222 if (obj2 != NULL) {
1223 targetfound = true;
1224 sname = prop_string_cstring_nocopy(obj);
1225 error = sme_event_unregister(sname,
1226 PENVSYS_EVENT_USER_CRITMIN);
1227 if (error)
1228 break;
1229
1230 prop_dictionary_remove(dict, "critical-min-limit");
1231 break;
1232 }
1233
1234 /* did the user want to remove a critical max limit? */
1235 obj2 = prop_dictionary_get(udict, "remove-cmax-limit");
1236 if (obj2 != NULL) {
1237 targetfound = true;
1238 sname = prop_string_cstring_nocopy(obj);
1239 error = sme_event_unregister(sname,
1240 PENVSYS_EVENT_USER_CRITMAX);
1241 if (error)
1242 break;
1243
1244 prop_dictionary_remove(dict, "critical-max-limit");
1245 break;
1246 }
1247
1248 /* did the user want to change rfact? */
1249 obj2 = prop_dictionary_get(udict, "new-rfact");
1250 if (obj2 != NULL) {
1251 targetfound = true;
1252 if (edata->flags & ENVSYS_FCHANGERFACT)
1253 edata->rfact = prop_number_integer_value(obj2);
1254 else
1255 error = ENOTSUP;
1256
1257 break;
1258 }
1259
1260 sdt = sme_get_description_table(SME_DESC_UNITS);
1261 for (i = 0; sdt[i].type != -1; i++)
1262 if (sdt[i].type == edata->units)
1263 break;
1264
1265 /* did the user want to set a critical capacity event? */
1266 obj2 = prop_dictionary_get(udict, "critical-capacity");
1267 if (obj2 != NULL) {
1268 targetfound = true;
1269 if ((edata->flags & ENVSYS_FMONNOTSUPP) ||
1270 (edata->flags & ENVSYS_FPERCENT) == 0) {
1271 error = ENOTSUP;
1272 break;
1273 }
1274
1275 critval = prop_number_integer_value(obj2);
1276 error = sme_event_register(dict,
1277 edata,
1278 sme->sme_name,
1279 "critical-capacity",
1280 critval,
1281 PENVSYS_EVENT_BATT_USERCAP,
1282 sdt[i].crittype);
1283 break;
1284 }
1285
1286 /* did the user want to set a critical max event? */
1287 obj2 = prop_dictionary_get(udict, "critical-max-limit");
1288 if (obj2 != NULL) {
1289 targetfound = true;
1290 if (edata->units == ENVSYS_INDICATOR ||
1291 edata->flags & ENVSYS_FMONNOTSUPP) {
1292 error = ENOTSUP;
1293 break;
1294 }
1295
1296 critval = prop_number_integer_value(obj2);
1297 error = sme_event_register(dict,
1298 edata,
1299 sme->sme_name,
1300 "critical-max-limit",
1301 critval,
1302 PENVSYS_EVENT_USER_CRITMAX,
1303 sdt[i].crittype);
1304 break;
1305 }
1306
1307 /* did the user want to set a critical min event? */
1308 obj2 = prop_dictionary_get(udict, "critical-min-limit");
1309 if (obj2 != NULL) {
1310 targetfound = true;
1311 if (edata->units == ENVSYS_INDICATOR ||
1312 edata->flags & ENVSYS_FMONNOTSUPP) {
1313 error = ENOTSUP;
1314 break;
1315 }
1316
1317 critval = prop_number_integer_value(obj2);
1318 error = sme_event_register(dict,
1319 edata,
1320 sme->sme_name,
1321 "critical-min-limit",
1322 critval,
1323 PENVSYS_EVENT_USER_CRITMIN,
1324 sdt[i].crittype);
1325 break;
1326 }
1327 }
1328
1329 /* invalid target? return the error */
1330 if (!targetfound)
1331 error = EINVAL;
1332
1333 return error;
1334 }
1335