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