1 | /* $NetBSD: sysmon_envsys.c,v 1.139 2015/12/14 01:08:47 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2007, 2008 Juan Romero Pardines. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | /*- |
29 | * Copyright (c) 2000 Zembu Labs, Inc. |
30 | * All rights reserved. |
31 | * |
32 | * Author: Jason R. Thorpe <thorpej@zembu.com> |
33 | * |
34 | * Redistribution and use in source and binary forms, with or without |
35 | * modification, are permitted provided that the following conditions |
36 | * are met: |
37 | * 1. Redistributions of source code must retain the above copyright |
38 | * notice, this list of conditions and the following disclaimer. |
39 | * 2. Redistributions in binary form must reproduce the above copyright |
40 | * notice, this list of conditions and the following disclaimer in the |
41 | * documentation and/or other materials provided with the distribution. |
42 | * 3. All advertising materials mentioning features or use of this software |
43 | * must display the following acknowledgement: |
44 | * This product includes software developed by Zembu Labs, Inc. |
45 | * 4. Neither the name of Zembu Labs nor the names of its employees may |
46 | * be used to endorse or promote products derived from this software |
47 | * without specific prior written permission. |
48 | * |
49 | * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS |
50 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- |
51 | * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- |
52 | * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, |
53 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
54 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
55 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
56 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
57 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
58 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
59 | */ |
60 | |
61 | /* |
62 | * Environmental sensor framework for sysmon, exported to userland |
63 | * with proplib(3). |
64 | */ |
65 | |
66 | #include <sys/cdefs.h> |
67 | __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.139 2015/12/14 01:08:47 pgoyette Exp $" ); |
68 | |
69 | #include <sys/param.h> |
70 | #include <sys/types.h> |
71 | #include <sys/conf.h> |
72 | #include <sys/errno.h> |
73 | #include <sys/fcntl.h> |
74 | #include <sys/kernel.h> |
75 | #include <sys/systm.h> |
76 | #include <sys/proc.h> |
77 | #include <sys/mutex.h> |
78 | #include <sys/kmem.h> |
79 | #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 |
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 |
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 |
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 |
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 |
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 |
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_cstring_nocopy(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 * |
524 | sysmon_envsys_create(void) |
525 | { |
526 | struct sysmon_envsys *sme; |
527 | |
528 | sme = kmem_zalloc(sizeof(*sme), KM_SLEEP); |
529 | TAILQ_INIT(&sme->sme_sensors_list); |
530 | LIST_INIT(&sme->sme_events_list); |
531 | mutex_init(&sme->sme_mtx, MUTEX_DEFAULT, IPL_NONE); |
532 | mutex_init(&sme->sme_work_mtx, MUTEX_DEFAULT, IPL_NONE); |
533 | cv_init(&sme->sme_condvar, "sme_wait" ); |
534 | |
535 | return sme; |
536 | } |
537 | |
538 | /* |
539 | * sysmon_envsys_destroy: |
540 | * |
541 | * + Removes all sensors from the tail queue, destroys the callout |
542 | * and frees the sysmon_envsys object. |
543 | */ |
544 | void |
545 | sysmon_envsys_destroy(struct sysmon_envsys *sme) |
546 | { |
547 | envsys_data_t *edata; |
548 | |
549 | KASSERT(sme != NULL); |
550 | |
551 | while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { |
552 | edata = TAILQ_FIRST(&sme->sme_sensors_list); |
553 | TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); |
554 | } |
555 | mutex_destroy(&sme->sme_mtx); |
556 | mutex_destroy(&sme->sme_work_mtx); |
557 | cv_destroy(&sme->sme_condvar); |
558 | kmem_free(sme, sizeof(*sme)); |
559 | } |
560 | |
561 | /* |
562 | * sysmon_envsys_sensor_attach: |
563 | * |
564 | * + Attaches a sensor into a sysmon_envsys device checking that units |
565 | * is set to a valid type and description is unique and not empty. |
566 | */ |
567 | int |
568 | sysmon_envsys_sensor_attach(struct sysmon_envsys *sme, envsys_data_t *edata) |
569 | { |
570 | const struct sme_descr_entry *sdt_units; |
571 | envsys_data_t *oedata; |
572 | |
573 | KASSERT(sme != NULL || edata != NULL); |
574 | |
575 | /* |
576 | * Find the correct units for this sensor. |
577 | */ |
578 | sdt_units = sme_find_table_entry(SME_DESC_UNITS, edata->units); |
579 | if (sdt_units->type == -1) |
580 | return EINVAL; |
581 | |
582 | /* |
583 | * Check that description is not empty or duplicate. |
584 | */ |
585 | if (strlen(edata->desc) == 0) |
586 | return EINVAL; |
587 | |
588 | mutex_enter(&sme->sme_mtx); |
589 | sysmon_envsys_acquire(sme, true); |
590 | TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { |
591 | if (strcmp(oedata->desc, edata->desc) == 0) { |
592 | sysmon_envsys_release(sme, true); |
593 | mutex_exit(&sme->sme_mtx); |
594 | return EEXIST; |
595 | } |
596 | } |
597 | /* |
598 | * Ok, the sensor has been added into the device queue. |
599 | */ |
600 | TAILQ_INSERT_TAIL(&sme->sme_sensors_list, edata, sensors_head); |
601 | |
602 | /* |
603 | * Give the sensor an index position. |
604 | */ |
605 | edata->sensor = sme->sme_nsensors; |
606 | sme->sme_nsensors++; |
607 | sysmon_envsys_release(sme, true); |
608 | mutex_exit(&sme->sme_mtx); |
609 | |
610 | DPRINTF(("%s: attached #%d (%s), units=%d (%s)\n" , |
611 | __func__, edata->sensor, edata->desc, |
612 | sdt_units->type, sdt_units->desc)); |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | /* |
618 | * sysmon_envsys_sensor_detach: |
619 | * |
620 | * + Detachs a sensor from a sysmon_envsys device and decrements the |
621 | * sensors count on success. |
622 | */ |
623 | int |
624 | sysmon_envsys_sensor_detach(struct sysmon_envsys *sme, envsys_data_t *edata) |
625 | { |
626 | envsys_data_t *oedata; |
627 | bool found = false; |
628 | bool destroy = false; |
629 | |
630 | KASSERT(sme != NULL || edata != NULL); |
631 | |
632 | /* |
633 | * Check the sensor is already on the list. |
634 | */ |
635 | mutex_enter(&sme->sme_mtx); |
636 | sysmon_envsys_acquire(sme, true); |
637 | TAILQ_FOREACH(oedata, &sme->sme_sensors_list, sensors_head) { |
638 | if (oedata->sensor == edata->sensor) { |
639 | found = true; |
640 | break; |
641 | } |
642 | } |
643 | |
644 | if (!found) { |
645 | sysmon_envsys_release(sme, true); |
646 | mutex_exit(&sme->sme_mtx); |
647 | return EINVAL; |
648 | } |
649 | |
650 | /* |
651 | * remove it, unhook from rnd(4), and decrement the sensors count. |
652 | */ |
653 | sme_event_unregister_sensor(sme, edata); |
654 | if (LIST_EMPTY(&sme->sme_events_list)) { |
655 | sme_events_halt_callout(sme); |
656 | destroy = true; |
657 | } |
658 | TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); |
659 | sme->sme_nsensors--; |
660 | sysmon_envsys_release(sme, true); |
661 | mutex_exit(&sme->sme_mtx); |
662 | |
663 | if (destroy) |
664 | sme_events_destroy(sme); |
665 | |
666 | return 0; |
667 | } |
668 | |
669 | |
670 | /* |
671 | * sysmon_envsys_register: |
672 | * |
673 | * + Register a sysmon envsys device. |
674 | * + Create array of dictionaries for a device. |
675 | */ |
676 | int |
677 | sysmon_envsys_register(struct sysmon_envsys *sme) |
678 | { |
679 | struct sme_evdrv { |
680 | SLIST_ENTRY(sme_evdrv) evdrv_head; |
681 | sme_event_drv_t *evdrv; |
682 | }; |
683 | SLIST_HEAD(, sme_evdrv) sme_evdrv_list; |
684 | struct sme_evdrv *evdv = NULL; |
685 | struct sysmon_envsys *lsme; |
686 | prop_array_t array = NULL; |
687 | prop_dictionary_t dict, dict2; |
688 | envsys_data_t *edata = NULL; |
689 | sme_event_drv_t *this_evdrv; |
690 | int nevent; |
691 | int error = 0; |
692 | char rnd_name[sizeof(edata->rnd_src.name)]; |
693 | |
694 | KASSERT(sme != NULL); |
695 | KASSERT(sme->sme_name != NULL); |
696 | |
697 | (void)RUN_ONCE(&once_envsys, sme_preinit); |
698 | |
699 | /* |
700 | * Check if requested sysmon_envsys device is valid |
701 | * and does not exist already in the list. |
702 | */ |
703 | mutex_enter(&sme_global_mtx); |
704 | LIST_FOREACH(lsme, &sysmon_envsys_list, sme_list) { |
705 | if (strcmp(lsme->sme_name, sme->sme_name) == 0) { |
706 | mutex_exit(&sme_global_mtx); |
707 | return EEXIST; |
708 | } |
709 | } |
710 | mutex_exit(&sme_global_mtx); |
711 | |
712 | /* |
713 | * sanity check: if SME_DISABLE_REFRESH is not set, |
714 | * the sme_refresh function callback must be non NULL. |
715 | */ |
716 | if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) |
717 | if (!sme->sme_refresh) |
718 | return EINVAL; |
719 | |
720 | /* |
721 | * If the list of sensors is empty, there's no point to continue... |
722 | */ |
723 | if (TAILQ_EMPTY(&sme->sme_sensors_list)) { |
724 | DPRINTF(("%s: sensors list empty for %s\n" , __func__, |
725 | sme->sme_name)); |
726 | return ENOTSUP; |
727 | } |
728 | |
729 | /* |
730 | * Initialize the singly linked list for driver events. |
731 | */ |
732 | SLIST_INIT(&sme_evdrv_list); |
733 | |
734 | array = prop_array_create(); |
735 | if (!array) |
736 | return ENOMEM; |
737 | |
738 | /* |
739 | * Iterate over all sensors and create a dictionary per sensor. |
740 | * We must respect the order in which the sensors were added. |
741 | */ |
742 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
743 | dict = prop_dictionary_create(); |
744 | if (!dict) { |
745 | error = ENOMEM; |
746 | goto out2; |
747 | } |
748 | |
749 | /* |
750 | * Create all objects in sensor's dictionary. |
751 | */ |
752 | this_evdrv = sme_add_sensor_dictionary(sme, array, |
753 | dict, edata); |
754 | if (this_evdrv) { |
755 | evdv = kmem_zalloc(sizeof(*evdv), KM_SLEEP); |
756 | evdv->evdrv = this_evdrv; |
757 | SLIST_INSERT_HEAD(&sme_evdrv_list, evdv, evdrv_head); |
758 | } |
759 | } |
760 | |
761 | /* |
762 | * If the array does not contain any object (sensor), there's |
763 | * no need to attach the driver. |
764 | */ |
765 | if (prop_array_count(array) == 0) { |
766 | error = EINVAL; |
767 | DPRINTF(("%s: empty array for '%s'\n" , __func__, |
768 | sme->sme_name)); |
769 | goto out; |
770 | } |
771 | |
772 | /* |
773 | * Add the dictionary for the global properties of this device. |
774 | */ |
775 | dict2 = prop_dictionary_create(); |
776 | if (!dict2) { |
777 | error = ENOMEM; |
778 | goto out; |
779 | } |
780 | |
781 | error = sme_add_property_dictionary(sme, array, dict2); |
782 | if (error) { |
783 | prop_object_release(dict2); |
784 | goto out; |
785 | } |
786 | |
787 | /* |
788 | * Add the array into the global dictionary for the driver. |
789 | * |
790 | * <dict> |
791 | * <key>foo0</key> |
792 | * <array> |
793 | * ... |
794 | */ |
795 | mutex_enter(&sme_global_mtx); |
796 | if (!prop_dictionary_set(sme_propd, sme->sme_name, array)) { |
797 | error = EINVAL; |
798 | mutex_exit(&sme_global_mtx); |
799 | DPRINTF(("%s: prop_dictionary_set for '%s'\n" , __func__, |
800 | sme->sme_name)); |
801 | goto out; |
802 | } |
803 | |
804 | /* |
805 | * Add the device into the list. |
806 | */ |
807 | LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); |
808 | sme->sme_fsensor = sysmon_envsys_next_sensor_index; |
809 | sysmon_envsys_next_sensor_index += sme->sme_nsensors; |
810 | mutex_exit(&sme_global_mtx); |
811 | |
812 | out: |
813 | /* |
814 | * No errors? Make an initial data refresh if was requested, |
815 | * then register the events that were set in the driver. Do |
816 | * the refresh first in case it is needed to establish the |
817 | * limits or max_value needed by some events. |
818 | */ |
819 | if (error == 0) { |
820 | nevent = 0; |
821 | |
822 | if (sme->sme_flags & SME_INIT_REFRESH) { |
823 | sysmon_task_queue_sched(0, sme_initial_refresh, sme); |
824 | DPRINTF(("%s: scheduled initial refresh for '%s'\n" , |
825 | __func__, sme->sme_name)); |
826 | } |
827 | SLIST_FOREACH(evdv, &sme_evdrv_list, evdrv_head) { |
828 | sysmon_task_queue_sched(0, |
829 | sme_event_drvadd, evdv->evdrv); |
830 | nevent++; |
831 | } |
832 | /* |
833 | * Hook the sensor into rnd(4) entropy pool if requested |
834 | */ |
835 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
836 | if (edata->flags & ENVSYS_FHAS_ENTROPY) { |
837 | uint32_t rnd_type, rnd_flag = 0; |
838 | size_t n; |
839 | int tail = 1; |
840 | |
841 | snprintf(rnd_name, sizeof(rnd_name), "%s-%s" , |
842 | sme->sme_name, edata->desc); |
843 | n = strlen(rnd_name); |
844 | /* |
845 | * 1) Remove trailing white space(s). |
846 | * 2) If space exist, replace it with '-' |
847 | */ |
848 | while (--n) { |
849 | if (rnd_name[n] == ' ') { |
850 | if (tail != 0) |
851 | rnd_name[n] = '\0'; |
852 | else |
853 | rnd_name[n] = '-'; |
854 | } else |
855 | tail = 0; |
856 | } |
857 | rnd_flag |= RND_FLAG_COLLECT_TIME; |
858 | rnd_flag |= RND_FLAG_ESTIMATE_TIME; |
859 | |
860 | switch (edata->units) { |
861 | case ENVSYS_STEMP: |
862 | case ENVSYS_SFANRPM: |
863 | case ENVSYS_INTEGER: |
864 | rnd_type = RND_TYPE_ENV; |
865 | rnd_flag |= RND_FLAG_COLLECT_VALUE; |
866 | rnd_flag |= RND_FLAG_ESTIMATE_VALUE; |
867 | break; |
868 | case ENVSYS_SVOLTS_AC: |
869 | case ENVSYS_SVOLTS_DC: |
870 | case ENVSYS_SOHMS: |
871 | case ENVSYS_SWATTS: |
872 | case ENVSYS_SAMPS: |
873 | case ENVSYS_SWATTHOUR: |
874 | case ENVSYS_SAMPHOUR: |
875 | rnd_type = RND_TYPE_POWER; |
876 | rnd_flag |= RND_FLAG_COLLECT_VALUE; |
877 | rnd_flag |= RND_FLAG_ESTIMATE_VALUE; |
878 | break; |
879 | default: |
880 | rnd_type = RND_TYPE_UNKNOWN; |
881 | break; |
882 | } |
883 | rnd_attach_source(&edata->rnd_src, rnd_name, |
884 | rnd_type, rnd_flag); |
885 | } |
886 | } |
887 | DPRINTF(("%s: driver '%s' registered (nsens=%d nevent=%d)\n" , |
888 | __func__, sme->sme_name, sme->sme_nsensors, nevent)); |
889 | } |
890 | |
891 | out2: |
892 | while (!SLIST_EMPTY(&sme_evdrv_list)) { |
893 | evdv = SLIST_FIRST(&sme_evdrv_list); |
894 | SLIST_REMOVE_HEAD(&sme_evdrv_list, evdrv_head); |
895 | kmem_free(evdv, sizeof(*evdv)); |
896 | } |
897 | if (!error) |
898 | return 0; |
899 | |
900 | /* |
901 | * Ugh... something wasn't right; unregister all events and sensors |
902 | * previously assigned and destroy the array with all its objects. |
903 | */ |
904 | DPRINTF(("%s: failed to register '%s' (%d)\n" , __func__, |
905 | sme->sme_name, error)); |
906 | |
907 | sme_event_unregister_all(sme); |
908 | while (!TAILQ_EMPTY(&sme->sme_sensors_list)) { |
909 | edata = TAILQ_FIRST(&sme->sme_sensors_list); |
910 | TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); |
911 | } |
912 | sysmon_envsys_destroy_plist(array); |
913 | return error; |
914 | } |
915 | |
916 | /* |
917 | * sysmon_envsys_destroy_plist: |
918 | * |
919 | * + Remove all objects from the array of dictionaries that is |
920 | * created in a sysmon envsys device. |
921 | */ |
922 | static void |
923 | sysmon_envsys_destroy_plist(prop_array_t array) |
924 | { |
925 | prop_object_iterator_t iter, iter2; |
926 | prop_dictionary_t dict; |
927 | prop_object_t obj; |
928 | |
929 | KASSERT(array != NULL); |
930 | KASSERT(prop_object_type(array) == PROP_TYPE_ARRAY); |
931 | |
932 | DPRINTFOBJ(("%s: objects in array=%d\n" , __func__, |
933 | prop_array_count(array))); |
934 | |
935 | iter = prop_array_iterator(array); |
936 | if (!iter) |
937 | return; |
938 | |
939 | while ((dict = prop_object_iterator_next(iter))) { |
940 | KASSERT(prop_object_type(dict) == PROP_TYPE_DICTIONARY); |
941 | iter2 = prop_dictionary_iterator(dict); |
942 | if (!iter2) |
943 | goto out; |
944 | DPRINTFOBJ(("%s: iterating over dictionary\n" , __func__)); |
945 | while ((obj = prop_object_iterator_next(iter2)) != NULL) { |
946 | DPRINTFOBJ(("%s: obj=%s\n" , __func__, |
947 | prop_dictionary_keysym_cstring_nocopy(obj))); |
948 | prop_dictionary_remove(dict, |
949 | prop_dictionary_keysym_cstring_nocopy(obj)); |
950 | prop_object_iterator_reset(iter2); |
951 | } |
952 | prop_object_iterator_release(iter2); |
953 | DPRINTFOBJ(("%s: objects in dictionary:%d\n" , |
954 | __func__, prop_dictionary_count(dict))); |
955 | prop_object_release(dict); |
956 | } |
957 | |
958 | out: |
959 | prop_object_iterator_release(iter); |
960 | prop_object_release(array); |
961 | } |
962 | |
963 | /* |
964 | * sysmon_envsys_unregister: |
965 | * |
966 | * + Unregister a sysmon envsys device. |
967 | */ |
968 | void |
969 | sysmon_envsys_unregister(struct sysmon_envsys *sme) |
970 | { |
971 | prop_array_t array; |
972 | struct sysmon_envsys *osme; |
973 | |
974 | KASSERT(sme != NULL); |
975 | |
976 | /* |
977 | * Decrement global sensors counter and the first_sensor index |
978 | * for remaining devices in the list (only used for compatibility |
979 | * with previous API), and remove the device from the list. |
980 | */ |
981 | mutex_enter(&sme_global_mtx); |
982 | sysmon_envsys_next_sensor_index -= sme->sme_nsensors; |
983 | LIST_FOREACH(osme, &sysmon_envsys_list, sme_list) { |
984 | if (osme->sme_fsensor >= sme->sme_fsensor) |
985 | osme->sme_fsensor -= sme->sme_nsensors; |
986 | } |
987 | LIST_REMOVE(sme, sme_list); |
988 | mutex_exit(&sme_global_mtx); |
989 | |
990 | /* |
991 | * Unregister all events associated with device. |
992 | */ |
993 | sme_event_unregister_all(sme); |
994 | |
995 | /* |
996 | * Remove the device (and all its objects) from the global dictionary. |
997 | */ |
998 | array = prop_dictionary_get(sme_propd, sme->sme_name); |
999 | if (array && prop_object_type(array) == PROP_TYPE_ARRAY) { |
1000 | mutex_enter(&sme_global_mtx); |
1001 | prop_dictionary_remove(sme_propd, sme->sme_name); |
1002 | mutex_exit(&sme_global_mtx); |
1003 | sysmon_envsys_destroy_plist(array); |
1004 | } |
1005 | /* |
1006 | * And finally destroy the sysmon_envsys object. |
1007 | */ |
1008 | sysmon_envsys_destroy(sme); |
1009 | } |
1010 | |
1011 | /* |
1012 | * sysmon_envsys_find: |
1013 | * |
1014 | * + Find a sysmon envsys device and mark it as busy |
1015 | * once it's available. |
1016 | */ |
1017 | struct sysmon_envsys * |
1018 | sysmon_envsys_find(const char *name) |
1019 | { |
1020 | struct sysmon_envsys *sme; |
1021 | |
1022 | mutex_enter(&sme_global_mtx); |
1023 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1024 | if (strcmp(sme->sme_name, name) == 0) { |
1025 | sysmon_envsys_acquire(sme, false); |
1026 | break; |
1027 | } |
1028 | } |
1029 | mutex_exit(&sme_global_mtx); |
1030 | |
1031 | return sme; |
1032 | } |
1033 | |
1034 | /* |
1035 | * Compatibility function with the old API. |
1036 | */ |
1037 | struct sysmon_envsys * |
1038 | sysmon_envsys_find_40(u_int idx) |
1039 | { |
1040 | struct sysmon_envsys *sme; |
1041 | |
1042 | mutex_enter(&sme_global_mtx); |
1043 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1044 | if (idx >= sme->sme_fsensor && |
1045 | idx < (sme->sme_fsensor + sme->sme_nsensors)) { |
1046 | sysmon_envsys_acquire(sme, false); |
1047 | break; |
1048 | } |
1049 | } |
1050 | mutex_exit(&sme_global_mtx); |
1051 | |
1052 | return sme; |
1053 | } |
1054 | |
1055 | /* |
1056 | * sysmon_envsys_acquire: |
1057 | * |
1058 | * + Wait until a sysmon envsys device is available and mark |
1059 | * it as busy. |
1060 | */ |
1061 | void |
1062 | sysmon_envsys_acquire(struct sysmon_envsys *sme, bool locked) |
1063 | { |
1064 | KASSERT(sme != NULL); |
1065 | |
1066 | if (locked) { |
1067 | while (sme->sme_flags & SME_FLAG_BUSY) |
1068 | cv_wait(&sme->sme_condvar, &sme->sme_mtx); |
1069 | sme->sme_flags |= SME_FLAG_BUSY; |
1070 | } else { |
1071 | mutex_enter(&sme->sme_mtx); |
1072 | while (sme->sme_flags & SME_FLAG_BUSY) |
1073 | cv_wait(&sme->sme_condvar, &sme->sme_mtx); |
1074 | sme->sme_flags |= SME_FLAG_BUSY; |
1075 | mutex_exit(&sme->sme_mtx); |
1076 | } |
1077 | } |
1078 | |
1079 | /* |
1080 | * sysmon_envsys_release: |
1081 | * |
1082 | * + Unmark a sysmon envsys device as busy, and notify |
1083 | * waiters. |
1084 | */ |
1085 | void |
1086 | sysmon_envsys_release(struct sysmon_envsys *sme, bool locked) |
1087 | { |
1088 | KASSERT(sme != NULL); |
1089 | |
1090 | if (locked) { |
1091 | sme->sme_flags &= ~SME_FLAG_BUSY; |
1092 | cv_broadcast(&sme->sme_condvar); |
1093 | } else { |
1094 | mutex_enter(&sme->sme_mtx); |
1095 | sme->sme_flags &= ~SME_FLAG_BUSY; |
1096 | cv_broadcast(&sme->sme_condvar); |
1097 | mutex_exit(&sme->sme_mtx); |
1098 | } |
1099 | } |
1100 | |
1101 | /* |
1102 | * sme_initial_refresh: |
1103 | * |
1104 | * + Do an initial refresh of the sensors in a device just after |
1105 | * interrupts are enabled in the autoconf(9) process. |
1106 | * |
1107 | */ |
1108 | static void |
1109 | sme_initial_refresh(void *arg) |
1110 | { |
1111 | struct sysmon_envsys *sme = arg; |
1112 | envsys_data_t *edata; |
1113 | |
1114 | mutex_enter(&sme->sme_mtx); |
1115 | sysmon_envsys_acquire(sme, true); |
1116 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) |
1117 | sysmon_envsys_refresh_sensor(sme, edata); |
1118 | sysmon_envsys_release(sme, true); |
1119 | mutex_exit(&sme->sme_mtx); |
1120 | } |
1121 | |
1122 | /* |
1123 | * sme_sensor_dictionary_get: |
1124 | * |
1125 | * + Returns a dictionary of a device specified by its index |
1126 | * position. |
1127 | */ |
1128 | prop_dictionary_t |
1129 | sme_sensor_dictionary_get(prop_array_t array, const char *index) |
1130 | { |
1131 | prop_object_iterator_t iter; |
1132 | prop_dictionary_t dict; |
1133 | prop_object_t obj; |
1134 | |
1135 | KASSERT(array != NULL || index != NULL); |
1136 | |
1137 | iter = prop_array_iterator(array); |
1138 | if (!iter) |
1139 | return NULL; |
1140 | |
1141 | while ((dict = prop_object_iterator_next(iter))) { |
1142 | obj = prop_dictionary_get(dict, "index" ); |
1143 | if (prop_string_equals_cstring(obj, index)) |
1144 | break; |
1145 | } |
1146 | |
1147 | prop_object_iterator_release(iter); |
1148 | return dict; |
1149 | } |
1150 | |
1151 | /* |
1152 | * sme_remove_userprops: |
1153 | * |
1154 | * + Remove all properties from all devices that were set by |
1155 | * the ENVSYS_SETDICTIONARY ioctl. |
1156 | */ |
1157 | static void |
1158 | sme_remove_userprops(void) |
1159 | { |
1160 | struct sysmon_envsys *sme; |
1161 | prop_array_t array; |
1162 | prop_dictionary_t sdict; |
1163 | envsys_data_t *edata = NULL; |
1164 | char tmp[ENVSYS_DESCLEN]; |
1165 | char rnd_name[sizeof(edata->rnd_src.name)]; |
1166 | sysmon_envsys_lim_t lims; |
1167 | const struct sme_descr_entry *sdt_units; |
1168 | uint32_t props; |
1169 | int ptype; |
1170 | |
1171 | mutex_enter(&sme_global_mtx); |
1172 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1173 | sysmon_envsys_acquire(sme, false); |
1174 | array = prop_dictionary_get(sme_propd, sme->sme_name); |
1175 | |
1176 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1177 | (void)snprintf(tmp, sizeof(tmp), "sensor%d" , |
1178 | edata->sensor); |
1179 | sdict = sme_sensor_dictionary_get(array, tmp); |
1180 | KASSERT(sdict != NULL); |
1181 | |
1182 | ptype = 0; |
1183 | if (edata->upropset & PROP_BATTCAP) { |
1184 | prop_dictionary_remove(sdict, |
1185 | "critical-capacity" ); |
1186 | ptype = PENVSYS_EVENT_CAPACITY; |
1187 | } |
1188 | |
1189 | if (edata->upropset & PROP_BATTWARN) { |
1190 | prop_dictionary_remove(sdict, |
1191 | "warning-capacity" ); |
1192 | ptype = PENVSYS_EVENT_CAPACITY; |
1193 | } |
1194 | |
1195 | if (edata->upropset & PROP_BATTHIGH) { |
1196 | prop_dictionary_remove(sdict, |
1197 | "high-capacity" ); |
1198 | ptype = PENVSYS_EVENT_CAPACITY; |
1199 | } |
1200 | |
1201 | if (edata->upropset & PROP_BATTMAX) { |
1202 | prop_dictionary_remove(sdict, |
1203 | "maximum-capacity" ); |
1204 | ptype = PENVSYS_EVENT_CAPACITY; |
1205 | } |
1206 | if (edata->upropset & PROP_WARNMAX) { |
1207 | prop_dictionary_remove(sdict, "warning-max" ); |
1208 | ptype = PENVSYS_EVENT_LIMITS; |
1209 | } |
1210 | |
1211 | if (edata->upropset & PROP_WARNMIN) { |
1212 | prop_dictionary_remove(sdict, "warning-min" ); |
1213 | ptype = PENVSYS_EVENT_LIMITS; |
1214 | } |
1215 | |
1216 | if (edata->upropset & PROP_CRITMAX) { |
1217 | prop_dictionary_remove(sdict, "critical-max" ); |
1218 | ptype = PENVSYS_EVENT_LIMITS; |
1219 | } |
1220 | |
1221 | if (edata->upropset & PROP_CRITMIN) { |
1222 | prop_dictionary_remove(sdict, "critical-min" ); |
1223 | ptype = PENVSYS_EVENT_LIMITS; |
1224 | } |
1225 | if (edata->upropset & PROP_RFACT) { |
1226 | (void)sme_sensor_upint32(sdict, "rfact" , 0); |
1227 | edata->rfact = 0; |
1228 | } |
1229 | |
1230 | if (edata->upropset & PROP_DESC) |
1231 | (void)sme_sensor_upstring(sdict, |
1232 | "description" , edata->desc); |
1233 | |
1234 | if (ptype == 0) |
1235 | continue; |
1236 | |
1237 | /* |
1238 | * If there were any limit values removed, we |
1239 | * need to revert to initial limits. |
1240 | * |
1241 | * First, tell the driver that we need it to |
1242 | * restore any h/w limits which may have been |
1243 | * changed to stored, boot-time values. |
1244 | */ |
1245 | if (sme->sme_set_limits) { |
1246 | DPRINTF(("%s: reset limits for %s %s\n" , |
1247 | __func__, sme->sme_name, edata->desc)); |
1248 | (*sme->sme_set_limits)(sme, edata, NULL, NULL); |
1249 | } |
1250 | |
1251 | /* |
1252 | * Next, we need to retrieve those initial limits. |
1253 | */ |
1254 | props = 0; |
1255 | edata->upropset &= ~PROP_LIMITS; |
1256 | if (sme->sme_get_limits) { |
1257 | DPRINTF(("%s: retrieve limits for %s %s\n" , |
1258 | __func__, sme->sme_name, edata->desc)); |
1259 | lims = edata->limits; |
1260 | (*sme->sme_get_limits)(sme, edata, &lims, |
1261 | &props); |
1262 | } |
1263 | |
1264 | /* |
1265 | * Finally, remove any old limits event, then |
1266 | * install a new event (which will update the |
1267 | * dictionary) |
1268 | */ |
1269 | sme_event_unregister(sme, edata->desc, |
1270 | PENVSYS_EVENT_LIMITS); |
1271 | |
1272 | /* |
1273 | * Find the correct units for this sensor. |
1274 | */ |
1275 | sdt_units = sme_find_table_entry(SME_DESC_UNITS, |
1276 | edata->units); |
1277 | |
1278 | if (props & PROP_LIMITS) { |
1279 | DPRINTF(("%s: install limits for %s %s\n" , |
1280 | __func__, sme->sme_name, edata->desc)); |
1281 | |
1282 | sme_event_register(sdict, edata, sme, |
1283 | &lims, props, PENVSYS_EVENT_LIMITS, |
1284 | sdt_units->crittype); |
1285 | } |
1286 | if (edata->flags & ENVSYS_FHAS_ENTROPY) { |
1287 | sme_event_register(sdict, edata, sme, |
1288 | &lims, props, PENVSYS_EVENT_NULL, |
1289 | sdt_units->crittype); |
1290 | snprintf(rnd_name, sizeof(rnd_name), "%s-%s" , |
1291 | sme->sme_name, edata->desc); |
1292 | rnd_attach_source(&edata->rnd_src, rnd_name, |
1293 | RND_TYPE_ENV, RND_FLAG_COLLECT_VALUE| |
1294 | RND_FLAG_COLLECT_TIME| |
1295 | RND_FLAG_ESTIMATE_VALUE| |
1296 | RND_FLAG_ESTIMATE_TIME); |
1297 | } |
1298 | } |
1299 | |
1300 | /* |
1301 | * Restore default timeout value. |
1302 | */ |
1303 | sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; |
1304 | sme_schedule_callout(sme); |
1305 | sysmon_envsys_release(sme, false); |
1306 | } |
1307 | mutex_exit(&sme_global_mtx); |
1308 | } |
1309 | |
1310 | /* |
1311 | * sme_add_property_dictionary: |
1312 | * |
1313 | * + Add global properties into a device. |
1314 | */ |
1315 | static int |
1316 | sme_add_property_dictionary(struct sysmon_envsys *sme, prop_array_t array, |
1317 | prop_dictionary_t dict) |
1318 | { |
1319 | prop_dictionary_t pdict; |
1320 | const char *class; |
1321 | int error = 0; |
1322 | |
1323 | pdict = prop_dictionary_create(); |
1324 | if (!pdict) |
1325 | return EINVAL; |
1326 | |
1327 | /* |
1328 | * Add the 'refresh-timeout' and 'dev-class' objects into the |
1329 | * 'device-properties' dictionary. |
1330 | * |
1331 | * ... |
1332 | * <dict> |
1333 | * <key>device-properties</key> |
1334 | * <dict> |
1335 | * <key>refresh-timeout</key> |
1336 | * <integer>120</integer< |
1337 | * <key>device-class</key> |
1338 | * <string>class_name</string> |
1339 | * </dict> |
1340 | * </dict> |
1341 | * ... |
1342 | * |
1343 | */ |
1344 | if (sme->sme_events_timeout == 0) { |
1345 | sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; |
1346 | sme_schedule_callout(sme); |
1347 | } |
1348 | |
1349 | if (!prop_dictionary_set_uint64(pdict, "refresh-timeout" , |
1350 | sme->sme_events_timeout)) { |
1351 | error = EINVAL; |
1352 | goto out; |
1353 | } |
1354 | if (sme->sme_class == SME_CLASS_BATTERY) |
1355 | class = "battery" ; |
1356 | else if (sme->sme_class == SME_CLASS_ACADAPTER) |
1357 | class = "ac-adapter" ; |
1358 | else |
1359 | class = "other" ; |
1360 | if (!prop_dictionary_set_cstring_nocopy(pdict, "device-class" , class)) { |
1361 | error = EINVAL; |
1362 | goto out; |
1363 | } |
1364 | |
1365 | if (!prop_dictionary_set(dict, "device-properties" , pdict)) { |
1366 | error = EINVAL; |
1367 | goto out; |
1368 | } |
1369 | |
1370 | /* |
1371 | * Add the device dictionary into the sysmon envsys array. |
1372 | */ |
1373 | if (!prop_array_add(array, dict)) |
1374 | error = EINVAL; |
1375 | |
1376 | out: |
1377 | prop_object_release(pdict); |
1378 | return error; |
1379 | } |
1380 | |
1381 | /* |
1382 | * sme_add_sensor_dictionary: |
1383 | * |
1384 | * + Adds the sensor objects into the dictionary and returns a pointer |
1385 | * to a sme_event_drv_t object if a monitoring flag was set |
1386 | * (or NULL otherwise). |
1387 | */ |
1388 | static sme_event_drv_t * |
1389 | sme_add_sensor_dictionary(struct sysmon_envsys *sme, prop_array_t array, |
1390 | prop_dictionary_t dict, envsys_data_t *edata) |
1391 | { |
1392 | const struct sme_descr_entry *sdt; |
1393 | int error; |
1394 | sme_event_drv_t *sme_evdrv_t = NULL; |
1395 | char indexstr[ENVSYS_DESCLEN]; |
1396 | bool mon_supported, allow_rfact; |
1397 | |
1398 | /* |
1399 | * Add the index sensor string. |
1400 | * |
1401 | * ... |
1402 | * <key>index</eyr |
1403 | * <string>sensor0</string> |
1404 | * ... |
1405 | */ |
1406 | (void)snprintf(indexstr, sizeof(indexstr), "sensor%d" , edata->sensor); |
1407 | if (sme_sensor_upstring(dict, "index" , indexstr)) |
1408 | goto bad; |
1409 | |
1410 | /* |
1411 | * ... |
1412 | * <key>description</key> |
1413 | * <string>blah blah</string> |
1414 | * ... |
1415 | */ |
1416 | if (sme_sensor_upstring(dict, "description" , edata->desc)) |
1417 | goto bad; |
1418 | |
1419 | /* |
1420 | * Add the monitoring boolean object: |
1421 | * |
1422 | * ... |
1423 | * <key>monitoring-supported</key> |
1424 | * <true/> |
1425 | * ... |
1426 | * |
1427 | * always false on Battery {capacity,charge}, Drive and Indicator types. |
1428 | * They cannot be monitored. |
1429 | * |
1430 | */ |
1431 | if ((edata->flags & ENVSYS_FMONNOTSUPP) || |
1432 | (edata->units == ENVSYS_INDICATOR) || |
1433 | (edata->units == ENVSYS_DRIVE) || |
1434 | (edata->units == ENVSYS_BATTERY_CAPACITY) || |
1435 | (edata->units == ENVSYS_BATTERY_CHARGE)) |
1436 | mon_supported = false; |
1437 | else |
1438 | mon_supported = true; |
1439 | if (sme_sensor_upbool(dict, "monitoring-supported" , mon_supported)) |
1440 | goto out; |
1441 | |
1442 | /* |
1443 | * Add the allow-rfact boolean object, true if |
1444 | * ENVSYS_FCHANGERFACT is set, false otherwise. |
1445 | * |
1446 | * ... |
1447 | * <key>allow-rfact</key> |
1448 | * <true/> |
1449 | * ... |
1450 | */ |
1451 | if (edata->units == ENVSYS_SVOLTS_DC || |
1452 | edata->units == ENVSYS_SVOLTS_AC) { |
1453 | if (edata->flags & ENVSYS_FCHANGERFACT) |
1454 | allow_rfact = true; |
1455 | else |
1456 | allow_rfact = false; |
1457 | if (sme_sensor_upbool(dict, "allow-rfact" , allow_rfact)) |
1458 | goto out; |
1459 | } |
1460 | |
1461 | error = sme_update_sensor_dictionary(dict, edata, |
1462 | (edata->state == ENVSYS_SVALID)); |
1463 | if (error < 0) |
1464 | goto bad; |
1465 | else if (error) |
1466 | goto out; |
1467 | |
1468 | /* |
1469 | * ... |
1470 | * </dict> |
1471 | * |
1472 | * Add the dictionary into the array. |
1473 | * |
1474 | */ |
1475 | if (!prop_array_add(array, dict)) { |
1476 | DPRINTF(("%s: prop_array_add\n" , __func__)); |
1477 | goto bad; |
1478 | } |
1479 | |
1480 | /* |
1481 | * Register new event(s) if any monitoring flag was set or if |
1482 | * the sensor provides entropy for rnd(4). |
1483 | */ |
1484 | if (edata->flags & (ENVSYS_FMONANY | ENVSYS_FHAS_ENTROPY)) { |
1485 | sme_evdrv_t = kmem_zalloc(sizeof(*sme_evdrv_t), KM_SLEEP); |
1486 | sme_evdrv_t->sed_sdict = dict; |
1487 | sme_evdrv_t->sed_edata = edata; |
1488 | sme_evdrv_t->sed_sme = sme; |
1489 | sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); |
1490 | sme_evdrv_t->sed_powertype = sdt->crittype; |
1491 | } |
1492 | |
1493 | out: |
1494 | return sme_evdrv_t; |
1495 | |
1496 | bad: |
1497 | prop_object_release(dict); |
1498 | return NULL; |
1499 | } |
1500 | |
1501 | /* |
1502 | * Find the maximum of all currently reported values. |
1503 | * The provided callback decides whether a sensor is part of the |
1504 | * maximum calculation (by returning true) or ignored (callback |
1505 | * returns false). Example usage: callback selects temperature |
1506 | * sensors in a given thermal zone, the function calculates the |
1507 | * maximum currently reported temperature in this zone. |
1508 | * If the parameter "refresh" is true, new values will be aquired |
1509 | * from the hardware, if not, the last reported value will be used. |
1510 | */ |
1511 | uint32_t |
1512 | sysmon_envsys_get_max_value(bool (*predicate)(const envsys_data_t*), |
1513 | bool refresh) |
1514 | { |
1515 | struct sysmon_envsys *sme; |
1516 | uint32_t maxv, v; |
1517 | |
1518 | maxv = 0; |
1519 | mutex_enter(&sme_global_mtx); |
1520 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1521 | sysmon_envsys_acquire(sme, false); |
1522 | v = sme_get_max_value(sme, predicate, refresh); |
1523 | sysmon_envsys_release(sme, false); |
1524 | if (v > maxv) |
1525 | maxv = v; |
1526 | } |
1527 | mutex_exit(&sme_global_mtx); |
1528 | return maxv; |
1529 | } |
1530 | |
1531 | static uint32_t |
1532 | sme_get_max_value(struct sysmon_envsys *sme, |
1533 | bool (*predicate)(const envsys_data_t*), |
1534 | bool refresh) |
1535 | { |
1536 | envsys_data_t *edata; |
1537 | uint32_t maxv, v; |
1538 | |
1539 | /* |
1540 | * Iterate over all sensors that match the predicate |
1541 | */ |
1542 | maxv = 0; |
1543 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1544 | if (!(*predicate)(edata)) |
1545 | continue; |
1546 | |
1547 | /* |
1548 | * refresh sensor data |
1549 | */ |
1550 | mutex_enter(&sme->sme_mtx); |
1551 | sysmon_envsys_refresh_sensor(sme, edata); |
1552 | mutex_exit(&sme->sme_mtx); |
1553 | |
1554 | v = edata->value_cur; |
1555 | if (v > maxv) |
1556 | maxv = v; |
1557 | |
1558 | } |
1559 | |
1560 | return maxv; |
1561 | } |
1562 | |
1563 | /* |
1564 | * sme_update_dictionary: |
1565 | * |
1566 | * + Update per-sensor dictionaries with new values if there were |
1567 | * changes, otherwise the object in dictionary is untouched. |
1568 | */ |
1569 | int |
1570 | sme_update_dictionary(struct sysmon_envsys *sme) |
1571 | { |
1572 | envsys_data_t *edata; |
1573 | prop_object_t array, dict, obj, obj2; |
1574 | int error = 0; |
1575 | |
1576 | /* |
1577 | * Retrieve the array of dictionaries in device. |
1578 | */ |
1579 | array = prop_dictionary_get(sme_propd, sme->sme_name); |
1580 | if (prop_object_type(array) != PROP_TYPE_ARRAY) { |
1581 | DPRINTF(("%s: not an array (%s)\n" , __func__, sme->sme_name)); |
1582 | return EINVAL; |
1583 | } |
1584 | |
1585 | /* |
1586 | * Get the last dictionary on the array, this contains the |
1587 | * 'device-properties' sub-dictionary. |
1588 | */ |
1589 | obj = prop_array_get(array, prop_array_count(array) - 1); |
1590 | if (!obj || prop_object_type(obj) != PROP_TYPE_DICTIONARY) { |
1591 | DPRINTF(("%s: not a device-properties dictionary\n" , __func__)); |
1592 | return EINVAL; |
1593 | } |
1594 | |
1595 | obj2 = prop_dictionary_get(obj, "device-properties" ); |
1596 | if (!obj2) |
1597 | return EINVAL; |
1598 | |
1599 | /* |
1600 | * Update the 'refresh-timeout' property. |
1601 | */ |
1602 | if (!prop_dictionary_set_uint64(obj2, "refresh-timeout" , |
1603 | sme->sme_events_timeout)) |
1604 | return EINVAL; |
1605 | |
1606 | /* |
1607 | * - iterate over all sensors. |
1608 | * - fetch new data. |
1609 | * - check if data in dictionary is different than new data. |
1610 | * - update dictionary if there were changes. |
1611 | */ |
1612 | DPRINTF(("%s: updating '%s' with nsensors=%d\n" , __func__, |
1613 | sme->sme_name, sme->sme_nsensors)); |
1614 | |
1615 | /* |
1616 | * Don't bother with locking when traversing the queue, |
1617 | * the device is already marked as busy; if a sensor |
1618 | * is going to be removed or added it will have to wait. |
1619 | */ |
1620 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1621 | /* |
1622 | * refresh sensor data via sme_envsys_refresh_sensor |
1623 | */ |
1624 | mutex_enter(&sme->sme_mtx); |
1625 | sysmon_envsys_refresh_sensor(sme, edata); |
1626 | mutex_exit(&sme->sme_mtx); |
1627 | |
1628 | /* |
1629 | * retrieve sensor's dictionary. |
1630 | */ |
1631 | dict = prop_array_get(array, edata->sensor); |
1632 | if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) { |
1633 | DPRINTF(("%s: not a dictionary (%d:%s)\n" , |
1634 | __func__, edata->sensor, sme->sme_name)); |
1635 | return EINVAL; |
1636 | } |
1637 | |
1638 | /* |
1639 | * update sensor's state. |
1640 | */ |
1641 | error = sme_update_sensor_dictionary(dict, edata, true); |
1642 | |
1643 | if (error) |
1644 | break; |
1645 | } |
1646 | |
1647 | return error; |
1648 | } |
1649 | |
1650 | int |
1651 | sme_update_sensor_dictionary(prop_object_t dict, envsys_data_t *edata, |
1652 | bool value_update) |
1653 | { |
1654 | const struct sme_descr_entry *sdt; |
1655 | int error = 0; |
1656 | |
1657 | sdt = sme_find_table_entry(SME_DESC_STATES, edata->state); |
1658 | if (sdt == NULL) { |
1659 | printf("sme_update_sensor_dictionary: cannot update sensor %d " |
1660 | "state %d unknown\n" , edata->sensor, edata->state); |
1661 | return EINVAL; |
1662 | } |
1663 | |
1664 | DPRINTFOBJ(("%s: sensor #%d type=%d (%s) flags=%d\n" , __func__, |
1665 | edata->sensor, sdt->type, sdt->desc, edata->flags)); |
1666 | |
1667 | error = sme_sensor_upstring(dict, "state" , sdt->desc); |
1668 | if (error) |
1669 | return (-error); |
1670 | |
1671 | /* |
1672 | * update sensor's type. |
1673 | */ |
1674 | sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); |
1675 | |
1676 | DPRINTFOBJ(("%s: sensor #%d units=%d (%s)\n" , __func__, edata->sensor, |
1677 | sdt->type, sdt->desc)); |
1678 | |
1679 | error = sme_sensor_upstring(dict, "type" , sdt->desc); |
1680 | if (error) |
1681 | return (-error); |
1682 | |
1683 | if (value_update) { |
1684 | /* |
1685 | * update sensor's current value. |
1686 | */ |
1687 | error = sme_sensor_upint32(dict, "cur-value" , edata->value_cur); |
1688 | if (error) |
1689 | return error; |
1690 | } |
1691 | |
1692 | /* |
1693 | * Battery charge and Indicator types do not |
1694 | * need the remaining objects, so skip them. |
1695 | */ |
1696 | if (edata->units == ENVSYS_INDICATOR || |
1697 | edata->units == ENVSYS_BATTERY_CHARGE) |
1698 | return error; |
1699 | |
1700 | /* |
1701 | * update sensor flags. |
1702 | */ |
1703 | if (edata->flags & ENVSYS_FPERCENT) { |
1704 | error = sme_sensor_upbool(dict, "want-percentage" , true); |
1705 | if (error) |
1706 | return error; |
1707 | } |
1708 | |
1709 | if (value_update) { |
1710 | /* |
1711 | * update sensor's {max,min}-value. |
1712 | */ |
1713 | if (edata->flags & ENVSYS_FVALID_MAX) { |
1714 | error = sme_sensor_upint32(dict, "max-value" , |
1715 | edata->value_max); |
1716 | if (error) |
1717 | return error; |
1718 | } |
1719 | |
1720 | if (edata->flags & ENVSYS_FVALID_MIN) { |
1721 | error = sme_sensor_upint32(dict, "min-value" , |
1722 | edata->value_min); |
1723 | if (error) |
1724 | return error; |
1725 | } |
1726 | |
1727 | /* |
1728 | * update 'rpms' only for ENVSYS_SFANRPM sensors. |
1729 | */ |
1730 | if (edata->units == ENVSYS_SFANRPM) { |
1731 | error = sme_sensor_upuint32(dict, "rpms" , edata->rpms); |
1732 | if (error) |
1733 | return error; |
1734 | } |
1735 | |
1736 | /* |
1737 | * update 'rfact' only for ENVSYS_SVOLTS_[AD]C sensors. |
1738 | */ |
1739 | if (edata->units == ENVSYS_SVOLTS_AC || |
1740 | edata->units == ENVSYS_SVOLTS_DC) { |
1741 | error = sme_sensor_upint32(dict, "rfact" , edata->rfact); |
1742 | if (error) |
1743 | return error; |
1744 | } |
1745 | } |
1746 | |
1747 | /* |
1748 | * update 'drive-state' only for ENVSYS_DRIVE sensors. |
1749 | */ |
1750 | if (edata->units == ENVSYS_DRIVE) { |
1751 | sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES, |
1752 | edata->value_cur); |
1753 | error = sme_sensor_upstring(dict, "drive-state" , sdt->desc); |
1754 | if (error) |
1755 | return error; |
1756 | } |
1757 | |
1758 | /* |
1759 | * update 'battery-capacity' only for ENVSYS_BATTERY_CAPACITY |
1760 | * sensors. |
1761 | */ |
1762 | if (edata->units == ENVSYS_BATTERY_CAPACITY) { |
1763 | sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY, |
1764 | edata->value_cur); |
1765 | error = sme_sensor_upstring(dict, "battery-capacity" , |
1766 | sdt->desc); |
1767 | if (error) |
1768 | return error; |
1769 | } |
1770 | |
1771 | return error; |
1772 | } |
1773 | |
1774 | /* |
1775 | * sme_userset_dictionary: |
1776 | * |
1777 | * + Parse the userland dictionary and run the appropiate tasks |
1778 | * that were specified. |
1779 | */ |
1780 | int |
1781 | sme_userset_dictionary(struct sysmon_envsys *sme, prop_dictionary_t udict, |
1782 | prop_array_t array) |
1783 | { |
1784 | const struct sme_descr_entry *sdt; |
1785 | envsys_data_t *edata; |
1786 | prop_dictionary_t dict, tdict = NULL; |
1787 | prop_object_t obj, obj1, obj2, tobj = NULL; |
1788 | uint32_t props; |
1789 | uint64_t refresh_timo = 0; |
1790 | sysmon_envsys_lim_t lims; |
1791 | int i, error = 0; |
1792 | const char *blah; |
1793 | bool targetfound = false; |
1794 | |
1795 | /* |
1796 | * The user wanted to change the refresh timeout value for this |
1797 | * device. |
1798 | * |
1799 | * Get the 'device-properties' object from the userland dictionary. |
1800 | */ |
1801 | obj = prop_dictionary_get(udict, "device-properties" ); |
1802 | if (obj && prop_object_type(obj) == PROP_TYPE_DICTIONARY) { |
1803 | /* |
1804 | * Get the 'refresh-timeout' property for this device. |
1805 | */ |
1806 | obj1 = prop_dictionary_get(obj, "refresh-timeout" ); |
1807 | if (obj1 && prop_object_type(obj1) == PROP_TYPE_NUMBER) { |
1808 | targetfound = true; |
1809 | refresh_timo = |
1810 | prop_number_unsigned_integer_value(obj1); |
1811 | if (refresh_timo < 1) |
1812 | error = EINVAL; |
1813 | else { |
1814 | mutex_enter(&sme->sme_mtx); |
1815 | if (sme->sme_events_timeout != refresh_timo) { |
1816 | sme->sme_events_timeout = refresh_timo; |
1817 | sme_schedule_callout(sme); |
1818 | } |
1819 | mutex_exit(&sme->sme_mtx); |
1820 | } |
1821 | } |
1822 | return error; |
1823 | |
1824 | } else if (!obj) { |
1825 | /* |
1826 | * Get sensor's index from userland dictionary. |
1827 | */ |
1828 | obj = prop_dictionary_get(udict, "index" ); |
1829 | if (!obj) |
1830 | return EINVAL; |
1831 | if (prop_object_type(obj) != PROP_TYPE_STRING) { |
1832 | DPRINTF(("%s: 'index' not a string\n" , __func__)); |
1833 | return EINVAL; |
1834 | } |
1835 | } else |
1836 | return EINVAL; |
1837 | |
1838 | /* |
1839 | * Don't bother with locking when traversing the queue, |
1840 | * the device is already marked as busy; if a sensor |
1841 | * is going to be removed or added it will have to wait. |
1842 | */ |
1843 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1844 | /* |
1845 | * Get a dictionary and check if it's our sensor by checking |
1846 | * at its index position. |
1847 | */ |
1848 | dict = prop_array_get(array, edata->sensor); |
1849 | obj1 = prop_dictionary_get(dict, "index" ); |
1850 | |
1851 | /* |
1852 | * is it our sensor? |
1853 | */ |
1854 | if (!prop_string_equals(obj1, obj)) |
1855 | continue; |
1856 | |
1857 | props = 0; |
1858 | |
1859 | /* |
1860 | * Check if a new description operation was |
1861 | * requested by the user and set new description. |
1862 | */ |
1863 | obj2 = prop_dictionary_get(udict, "description" ); |
1864 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_STRING) { |
1865 | targetfound = true; |
1866 | blah = prop_string_cstring_nocopy(obj2); |
1867 | |
1868 | /* |
1869 | * Check for duplicate description. |
1870 | */ |
1871 | for (i = 0; i < sme->sme_nsensors; i++) { |
1872 | if (i == edata->sensor) |
1873 | continue; |
1874 | tdict = prop_array_get(array, i); |
1875 | tobj = |
1876 | prop_dictionary_get(tdict, "description" ); |
1877 | if (prop_string_equals(obj2, tobj)) { |
1878 | error = EEXIST; |
1879 | goto out; |
1880 | } |
1881 | } |
1882 | |
1883 | /* |
1884 | * Update the object in dictionary. |
1885 | */ |
1886 | mutex_enter(&sme->sme_mtx); |
1887 | error = sme_sensor_upstring(dict, |
1888 | "description" , |
1889 | blah); |
1890 | if (error) { |
1891 | mutex_exit(&sme->sme_mtx); |
1892 | goto out; |
1893 | } |
1894 | |
1895 | DPRINTF(("%s: sensor%d changed desc to: %s\n" , |
1896 | __func__, edata->sensor, blah)); |
1897 | edata->upropset |= PROP_DESC; |
1898 | mutex_exit(&sme->sme_mtx); |
1899 | } |
1900 | |
1901 | /* |
1902 | * did the user want to change the rfact? |
1903 | */ |
1904 | obj2 = prop_dictionary_get(udict, "rfact" ); |
1905 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1906 | targetfound = true; |
1907 | if (edata->flags & ENVSYS_FCHANGERFACT) { |
1908 | mutex_enter(&sme->sme_mtx); |
1909 | edata->rfact = prop_number_integer_value(obj2); |
1910 | edata->upropset |= PROP_RFACT; |
1911 | mutex_exit(&sme->sme_mtx); |
1912 | DPRINTF(("%s: sensor%d changed rfact to %d\n" , |
1913 | __func__, edata->sensor, edata->rfact)); |
1914 | } else { |
1915 | error = ENOTSUP; |
1916 | goto out; |
1917 | } |
1918 | } |
1919 | |
1920 | sdt = sme_find_table_entry(SME_DESC_UNITS, edata->units); |
1921 | |
1922 | /* |
1923 | * did the user want to set a critical capacity event? |
1924 | */ |
1925 | obj2 = prop_dictionary_get(udict, "critical-capacity" ); |
1926 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1927 | targetfound = true; |
1928 | lims.sel_critmin = prop_number_integer_value(obj2); |
1929 | props |= PROP_BATTCAP; |
1930 | } |
1931 | |
1932 | /* |
1933 | * did the user want to set a warning capacity event? |
1934 | */ |
1935 | obj2 = prop_dictionary_get(udict, "warning-capacity" ); |
1936 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1937 | targetfound = true; |
1938 | lims.sel_warnmin = prop_number_integer_value(obj2); |
1939 | props |= PROP_BATTWARN; |
1940 | } |
1941 | |
1942 | /* |
1943 | * did the user want to set a high capacity event? |
1944 | */ |
1945 | obj2 = prop_dictionary_get(udict, "high-capacity" ); |
1946 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1947 | targetfound = true; |
1948 | lims.sel_warnmin = prop_number_integer_value(obj2); |
1949 | props |= PROP_BATTHIGH; |
1950 | } |
1951 | |
1952 | /* |
1953 | * did the user want to set a maximum capacity event? |
1954 | */ |
1955 | obj2 = prop_dictionary_get(udict, "maximum-capacity" ); |
1956 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1957 | targetfound = true; |
1958 | lims.sel_warnmin = prop_number_integer_value(obj2); |
1959 | props |= PROP_BATTMAX; |
1960 | } |
1961 | |
1962 | /* |
1963 | * did the user want to set a critical max event? |
1964 | */ |
1965 | obj2 = prop_dictionary_get(udict, "critical-max" ); |
1966 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1967 | targetfound = true; |
1968 | lims.sel_critmax = prop_number_integer_value(obj2); |
1969 | props |= PROP_CRITMAX; |
1970 | } |
1971 | |
1972 | /* |
1973 | * did the user want to set a warning max event? |
1974 | */ |
1975 | obj2 = prop_dictionary_get(udict, "warning-max" ); |
1976 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1977 | targetfound = true; |
1978 | lims.sel_warnmax = prop_number_integer_value(obj2); |
1979 | props |= PROP_WARNMAX; |
1980 | } |
1981 | |
1982 | /* |
1983 | * did the user want to set a critical min event? |
1984 | */ |
1985 | obj2 = prop_dictionary_get(udict, "critical-min" ); |
1986 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1987 | targetfound = true; |
1988 | lims.sel_critmin = prop_number_integer_value(obj2); |
1989 | props |= PROP_CRITMIN; |
1990 | } |
1991 | |
1992 | /* |
1993 | * did the user want to set a warning min event? |
1994 | */ |
1995 | obj2 = prop_dictionary_get(udict, "warning-min" ); |
1996 | if (obj2 && prop_object_type(obj2) == PROP_TYPE_NUMBER) { |
1997 | targetfound = true; |
1998 | lims.sel_warnmin = prop_number_integer_value(obj2); |
1999 | props |= PROP_WARNMIN; |
2000 | } |
2001 | |
2002 | if (props && (edata->flags & ENVSYS_FMONNOTSUPP) != 0) { |
2003 | error = ENOTSUP; |
2004 | goto out; |
2005 | } |
2006 | if (props || (edata->flags & ENVSYS_FHAS_ENTROPY) != 0) { |
2007 | error = sme_event_register(dict, edata, sme, &lims, |
2008 | props, |
2009 | (edata->flags & ENVSYS_FPERCENT)? |
2010 | PENVSYS_EVENT_CAPACITY: |
2011 | PENVSYS_EVENT_LIMITS, |
2012 | sdt->crittype); |
2013 | if (error == EEXIST) |
2014 | error = 0; |
2015 | if (error) |
2016 | goto out; |
2017 | } |
2018 | |
2019 | /* |
2020 | * All objects in dictionary were processed. |
2021 | */ |
2022 | break; |
2023 | } |
2024 | |
2025 | out: |
2026 | /* |
2027 | * invalid target? return the error. |
2028 | */ |
2029 | if (!targetfound) |
2030 | error = EINVAL; |
2031 | |
2032 | return error; |
2033 | } |
2034 | |
2035 | /* |
2036 | * + sysmon_envsys_foreach_sensor |
2037 | * |
2038 | * Walk through the devices' sensor lists and execute the callback. |
2039 | * If the callback returns false, the remainder of the current |
2040 | * device's sensors are skipped. |
2041 | */ |
2042 | void |
2043 | sysmon_envsys_foreach_sensor(sysmon_envsys_callback_t func, void *arg, |
2044 | bool refresh) |
2045 | { |
2046 | struct sysmon_envsys *sme; |
2047 | envsys_data_t *sensor; |
2048 | |
2049 | mutex_enter(&sme_global_mtx); |
2050 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
2051 | |
2052 | sysmon_envsys_acquire(sme, false); |
2053 | TAILQ_FOREACH(sensor, &sme->sme_sensors_list, sensors_head) { |
2054 | if (refresh) { |
2055 | mutex_enter(&sme->sme_mtx); |
2056 | sysmon_envsys_refresh_sensor(sme, sensor); |
2057 | mutex_exit(&sme->sme_mtx); |
2058 | } |
2059 | if (!(*func)(sme, sensor, arg)) |
2060 | break; |
2061 | } |
2062 | sysmon_envsys_release(sme, false); |
2063 | } |
2064 | mutex_exit(&sme_global_mtx); |
2065 | } |
2066 | |
2067 | /* |
2068 | * Call the sensor's refresh function, and collect/stir entropy |
2069 | */ |
2070 | void |
2071 | sysmon_envsys_refresh_sensor(struct sysmon_envsys *sme, envsys_data_t *edata) |
2072 | { |
2073 | |
2074 | if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) |
2075 | (*sme->sme_refresh)(sme, edata); |
2076 | |
2077 | if (edata->flags & ENVSYS_FHAS_ENTROPY && |
2078 | edata->state != ENVSYS_SINVALID && |
2079 | edata->value_prev != edata->value_cur) |
2080 | rnd_add_uint32(&edata->rnd_src, edata->value_cur); |
2081 | edata->value_prev = edata->value_cur; |
2082 | } |
2083 | |
2084 | static |
2085 | int |
2086 | sysmon_envsys_modcmd(modcmd_t cmd, void *arg) |
2087 | { |
2088 | int ret; |
2089 | |
2090 | switch (cmd) { |
2091 | case MODULE_CMD_INIT: |
2092 | ret = sysmon_envsys_init(); |
2093 | break; |
2094 | |
2095 | case MODULE_CMD_FINI: |
2096 | ret = sysmon_envsys_fini(); |
2097 | break; |
2098 | |
2099 | case MODULE_CMD_STAT: |
2100 | default: |
2101 | ret = ENOTTY; |
2102 | } |
2103 | |
2104 | return ret; |
2105 | } |
2106 | |