1 | /* $NetBSD: sysmon_envsys_events.c,v 1.118 2015/10/15 13:35:30 bouyer 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 | * sysmon_envsys(9) events framework. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.118 2015/10/15 13:35:30 bouyer Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/types.h> |
37 | #include <sys/conf.h> |
38 | #include <sys/errno.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/proc.h> |
42 | #include <sys/mutex.h> |
43 | #include <sys/kmem.h> |
44 | #include <sys/callout.h> |
45 | #include <sys/syslog.h> |
46 | |
47 | #include <dev/sysmon/sysmonvar.h> |
48 | #include <dev/sysmon/sysmon_envsysvar.h> |
49 | |
50 | struct sme_sensor_event { |
51 | int state; |
52 | int event; |
53 | }; |
54 | |
55 | static const struct sme_sensor_event sme_sensor_event[] = { |
56 | { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL }, |
57 | { ENVSYS_SCRITOVER, PENVSYS_EVENT_CRITOVER }, |
58 | { ENVSYS_SCRITUNDER, PENVSYS_EVENT_CRITUNDER }, |
59 | { ENVSYS_SWARNOVER, PENVSYS_EVENT_WARNOVER }, |
60 | { ENVSYS_SWARNUNDER, PENVSYS_EVENT_WARNUNDER }, |
61 | { ENVSYS_BATTERY_CAPACITY_NORMAL, PENVSYS_EVENT_NORMAL }, |
62 | { ENVSYS_BATTERY_CAPACITY_WARNING, PENVSYS_EVENT_BATT_WARN }, |
63 | { ENVSYS_BATTERY_CAPACITY_CRITICAL, PENVSYS_EVENT_BATT_CRIT }, |
64 | { ENVSYS_BATTERY_CAPACITY_HIGH, PENVSYS_EVENT_BATT_HIGH }, |
65 | { ENVSYS_BATTERY_CAPACITY_MAX, PENVSYS_EVENT_BATT_MAX }, |
66 | { -1, -1 } |
67 | }; |
68 | |
69 | static const struct op_t { |
70 | const char *name; |
71 | enum envsys_lims idx; |
72 | uint32_t prop; |
73 | } limit_ops[] = { |
74 | /* Value-based limits */ |
75 | { "critical-max" , ENVSYS_LIM_CRITMAX, PROP_CRITMAX }, |
76 | { "warning-max" , ENVSYS_LIM_WARNMAX, PROP_WARNMAX }, |
77 | { "warning-min" , ENVSYS_LIM_WARNMIN, PROP_WARNMIN }, |
78 | { "critical-min" , ENVSYS_LIM_CRITMIN, PROP_CRITMIN }, |
79 | |
80 | /* %Capacity-based limits */ |
81 | { "maximum-capacity" , ENVSYS_LIM_CRITMAX, PROP_BATTMAX }, |
82 | { "high-capacity" , ENVSYS_LIM_WARNMAX, PROP_BATTHIGH }, |
83 | { "warning-capacity" , ENVSYS_LIM_WARNMIN, PROP_BATTWARN }, |
84 | { "critical-capacity" , ENVSYS_LIM_CRITMIN, PROP_BATTCAP }, |
85 | { NULL, 0, 0 } |
86 | }; |
87 | |
88 | static const struct ev_reg_t { |
89 | uint32_t crittype; |
90 | uint32_t powertype; |
91 | const char *name; |
92 | } reg_events[] = { |
93 | { ENVSYS_FMONCRITICAL, PENVSYS_EVENT_CRITICAL, "critical" }, |
94 | { ENVSYS_FMONSTCHANGED, PENVSYS_EVENT_STATE_CHANGED, "state-changed" }, |
95 | { ENVSYS_FMONLIMITS, PENVSYS_EVENT_LIMITS, "hw-range-limits" }, |
96 | { ENVSYS_FHAS_ENTROPY, PENVSYS_EVENT_NULL, "refresh-event" }, |
97 | { 0, 0, NULL } |
98 | }; |
99 | |
100 | static bool sysmon_low_power; |
101 | |
102 | #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz) |
103 | |
104 | static bool sme_event_check_low_power(void); |
105 | static bool sme_battery_check(void); |
106 | static bool sme_battery_critical(envsys_data_t *); |
107 | static bool sme_acadapter_check(void); |
108 | |
109 | static void sme_remove_event(sme_event_t *, struct sysmon_envsys *); |
110 | |
111 | /* |
112 | * sme_event_register: |
113 | * |
114 | * + Registers a new sysmon envsys event or updates any event |
115 | * already in the queue. |
116 | */ |
117 | int |
118 | sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata, |
119 | struct sysmon_envsys *sme, sysmon_envsys_lim_t *lims, |
120 | uint32_t props, int crittype, int powertype) |
121 | { |
122 | sme_event_t *see = NULL, *osee = NULL; |
123 | prop_object_t obj; |
124 | int error = 0; |
125 | const char *objkey; |
126 | const struct op_t *op; |
127 | |
128 | KASSERT(sdict != NULL); |
129 | KASSERT(edata != NULL); |
130 | KASSERT(sme != NULL); |
131 | KASSERT(lims != NULL); |
132 | |
133 | /* |
134 | * Some validation first for limit-checking events |
135 | * |
136 | * 1. Limits are not permitted if the units is ENVSYS_INDICATOR |
137 | * or ENVSYS_BATTERY_CHARGE. |
138 | * |
139 | * 2. Capacity limits are permitted only if the sensor has the |
140 | * ENVSYS_FPERCENT flag set and value_max is set. |
141 | * |
142 | * 3. It is not permissible for both capacity and value limits |
143 | * to coexist. |
144 | * |
145 | * Note that it permissible for a sensor to have value limits |
146 | * even if its ENVSYS_FPERCENT flag and value_max are set. |
147 | */ |
148 | |
149 | DPRINTF(("%s: units %d props 0x%04x upropset 0x%04x max_val %d" |
150 | " edata-flags 0x%04x\n" , __func__, edata->units, props, |
151 | edata->upropset, edata->value_max, edata->flags)); |
152 | |
153 | if (props) |
154 | if (edata->units == ENVSYS_INDICATOR || |
155 | edata->units == ENVSYS_BATTERY_CHARGE) |
156 | return ENOTSUP; |
157 | |
158 | if ((props & PROP_CAP_LIMITS) && |
159 | ((edata->value_max == 0) || |
160 | !(edata->flags & ENVSYS_FPERCENT) || |
161 | (props & PROP_VAL_LIMITS) || |
162 | (edata->upropset & PROP_VAL_LIMITS))) |
163 | props = 0; |
164 | |
165 | if ((props & PROP_VAL_LIMITS) && (edata->upropset & PROP_CAP_LIMITS)) |
166 | props = 0; |
167 | |
168 | /* |
169 | * check if the event is already on the list and return |
170 | * EEXIST if value provided hasn't been changed. |
171 | */ |
172 | mutex_enter(&sme->sme_mtx); |
173 | LIST_FOREACH(osee, &sme->sme_events_list, see_list) { |
174 | if (strcmp(edata->desc, osee->see_pes.pes_sensname) != 0) |
175 | continue; |
176 | if (crittype != osee->see_type && |
177 | osee->see_type != PENVSYS_EVENT_NULL) |
178 | continue; |
179 | |
180 | /* |
181 | * We found an existing event for this sensor. Make |
182 | * sure it references the correct edata |
183 | */ |
184 | KASSERT(edata == osee->see_edata); |
185 | |
186 | DPRINTF(("%s: dev %s sensor %s: event type %d exists\n" , |
187 | __func__, sme->sme_name, edata->desc, crittype)); |
188 | |
189 | see = osee; |
190 | if (props & edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) { |
191 | if (lims->sel_critmax == edata->limits.sel_critmax) { |
192 | DPRINTF(("%s: critmax exists\n" , __func__)); |
193 | error = EEXIST; |
194 | props &= ~(PROP_CRITMAX | PROP_BATTMAX); |
195 | } |
196 | } |
197 | if (props & edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) { |
198 | if (lims->sel_warnmax == edata->limits.sel_warnmax) { |
199 | DPRINTF(("%s: warnmax exists\n" , __func__)); |
200 | error = EEXIST; |
201 | props &= ~(PROP_WARNMAX | PROP_BATTHIGH); |
202 | } |
203 | } |
204 | if (props & edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) { |
205 | if (lims->sel_warnmin == edata->limits.sel_warnmin) { |
206 | DPRINTF(("%s: warnmin exists\n" , __func__)); |
207 | error = EEXIST; |
208 | props &= ~(PROP_WARNMIN | PROP_BATTWARN); |
209 | } |
210 | } |
211 | if (props & edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) { |
212 | if (lims->sel_critmin == edata->limits.sel_critmin) { |
213 | DPRINTF(("%s: critmin exists\n" , __func__)); |
214 | error = EEXIST; |
215 | props &= ~(PROP_CRITMIN | PROP_BATTCAP); |
216 | } |
217 | } |
218 | if (props && see->see_type == PENVSYS_EVENT_NULL) |
219 | see->see_type = crittype; |
220 | |
221 | break; |
222 | } |
223 | if (crittype == PENVSYS_EVENT_NULL && see != NULL) { |
224 | mutex_exit(&sme->sme_mtx); |
225 | return EEXIST; |
226 | } |
227 | |
228 | if (see == NULL) { |
229 | /* |
230 | * New event requested - allocate a sysmon_envsys event. |
231 | */ |
232 | see = kmem_zalloc(sizeof(*see), KM_SLEEP); |
233 | if (see == NULL) |
234 | return ENOMEM; |
235 | |
236 | DPRINTF(("%s: dev %s sensor %s: new event\n" , |
237 | __func__, sme->sme_name, edata->desc)); |
238 | |
239 | see->see_type = crittype; |
240 | see->see_sme = sme; |
241 | see->see_edata = edata; |
242 | |
243 | /* Initialize sensor type and previously-sent state */ |
244 | |
245 | see->see_pes.pes_type = powertype; |
246 | |
247 | switch (crittype) { |
248 | case PENVSYS_EVENT_CAPACITY: |
249 | see->see_evstate = ENVSYS_BATTERY_CAPACITY_NORMAL; |
250 | break; |
251 | case PENVSYS_EVENT_STATE_CHANGED: |
252 | if (edata->units == ENVSYS_BATTERY_CAPACITY) |
253 | see->see_evstate = |
254 | ENVSYS_BATTERY_CAPACITY_NORMAL; |
255 | else if (edata->units == ENVSYS_DRIVE) |
256 | see->see_evstate = ENVSYS_DRIVE_EMPTY; |
257 | else if (edata->units == ENVSYS_INDICATOR) |
258 | see->see_evstate = ENVSYS_SVALID; |
259 | else |
260 | panic("%s: bad units for " |
261 | "PENVSYS_EVENT_STATE_CHANGED" , __func__); |
262 | break; |
263 | case PENVSYS_EVENT_CRITICAL: |
264 | case PENVSYS_EVENT_LIMITS: |
265 | default: |
266 | see->see_evstate = ENVSYS_SVALID; |
267 | break; |
268 | } |
269 | see->see_evvalue = 0; |
270 | |
271 | (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name, |
272 | sizeof(see->see_pes.pes_dvname)); |
273 | (void)strlcpy(see->see_pes.pes_sensname, edata->desc, |
274 | sizeof(see->see_pes.pes_sensname)); |
275 | } |
276 | |
277 | /* |
278 | * Limit operation requested. |
279 | */ |
280 | for (op = limit_ops; op->name != NULL; op++) { |
281 | if (props & op->prop) { |
282 | objkey = op->name; |
283 | obj = prop_dictionary_get(sdict, objkey); |
284 | if (obj != NULL && |
285 | prop_object_type(obj) != PROP_TYPE_NUMBER) { |
286 | DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n" , |
287 | __func__, sme->sme_name, objkey)); |
288 | error = ENOTSUP; |
289 | } else { |
290 | edata->limits.sel_limit_list[op->idx] = |
291 | lims->sel_limit_list[op->idx]; |
292 | error = sme_sensor_upint32(sdict, objkey, |
293 | lims->sel_limit_list[op->idx]); |
294 | DPRINTF(("%s: (%s) event [sensor=%s type=%d] " |
295 | "(%s updated)\n" , __func__, sme->sme_name, |
296 | edata->desc, crittype, objkey)); |
297 | } |
298 | if (error && error != EEXIST) |
299 | goto out; |
300 | edata->upropset |= op->prop; |
301 | } |
302 | } |
303 | |
304 | if (props & PROP_DRIVER_LIMITS) |
305 | edata->upropset |= PROP_DRIVER_LIMITS; |
306 | else |
307 | edata->upropset &= ~PROP_DRIVER_LIMITS; |
308 | |
309 | DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d " |
310 | "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32 |
311 | " critmax=%" PRIu32 " props 0x%04x)\n" , __func__, |
312 | see->see_sme->sme_name, see->see_pes.pes_sensname, |
313 | edata->sensor, see->see_type, edata->limits.sel_critmin, |
314 | edata->limits.sel_warnmin, edata->limits.sel_warnmax, |
315 | edata->limits.sel_critmax, edata->upropset)); |
316 | /* |
317 | * Initialize the events framework if it wasn't initialized before. |
318 | */ |
319 | if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) |
320 | error = sme_events_init(sme); |
321 | |
322 | /* |
323 | * If driver requested notification, advise it of new |
324 | * limit values |
325 | */ |
326 | if (sme->sme_set_limits) |
327 | (*sme->sme_set_limits)(sme, edata, &(edata->limits), |
328 | &(edata->upropset)); |
329 | |
330 | out: |
331 | if ((error == 0 || error == EEXIST) && osee == NULL) |
332 | LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list); |
333 | |
334 | mutex_exit(&sme->sme_mtx); |
335 | |
336 | return error; |
337 | } |
338 | |
339 | /* |
340 | * sme_event_unregister_all: |
341 | * |
342 | * + Unregisters all events associated with a sysmon envsys device. |
343 | */ |
344 | void |
345 | sme_event_unregister_all(struct sysmon_envsys *sme) |
346 | { |
347 | sme_event_t *see; |
348 | int evcounter = 0; |
349 | bool destroy = false; |
350 | |
351 | KASSERT(sme != NULL); |
352 | |
353 | mutex_enter(&sme->sme_mtx); |
354 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
355 | while (see->see_flags & SEE_EVENT_WORKING) |
356 | cv_wait(&sme->sme_condvar, &sme->sme_mtx); |
357 | |
358 | if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) |
359 | evcounter++; |
360 | } |
361 | |
362 | DPRINTF(("%s: total events %d (%s)\n" , __func__, |
363 | evcounter, sme->sme_name)); |
364 | |
365 | while ((see = LIST_FIRST(&sme->sme_events_list))) { |
366 | if (evcounter == 0) |
367 | break; |
368 | |
369 | if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) { |
370 | DPRINTF(("%s: event %s %d removed (%s)\n" , __func__, |
371 | see->see_pes.pes_sensname, see->see_type, |
372 | sme->sme_name)); |
373 | sme_remove_event(see, sme); |
374 | |
375 | evcounter--; |
376 | } |
377 | } |
378 | |
379 | if (LIST_EMPTY(&sme->sme_events_list) && |
380 | sme->sme_flags & SME_CALLOUT_INITIALIZED) { |
381 | sme_events_halt_callout(sme); |
382 | destroy = true; |
383 | } |
384 | mutex_exit(&sme->sme_mtx); |
385 | |
386 | if (destroy) |
387 | sme_events_destroy(sme); |
388 | } |
389 | |
390 | /* |
391 | * sme_event_unregister: |
392 | * |
393 | * + Unregisters an event from the specified sysmon envsys device. |
394 | */ |
395 | int |
396 | sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type) |
397 | { |
398 | sme_event_t *see; |
399 | bool found = false; |
400 | bool destroy = false; |
401 | |
402 | KASSERT(sensor != NULL); |
403 | |
404 | mutex_enter(&sme->sme_mtx); |
405 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
406 | if (strcmp(see->see_pes.pes_sensname, sensor) == 0) { |
407 | if (see->see_type == type) { |
408 | found = true; |
409 | break; |
410 | } |
411 | } |
412 | } |
413 | |
414 | if (!found) { |
415 | mutex_exit(&sme->sme_mtx); |
416 | return EINVAL; |
417 | } |
418 | |
419 | /* |
420 | * Wait for the event to finish its work, remove it from the list |
421 | * and release resources. |
422 | */ |
423 | while (see->see_flags & SEE_EVENT_WORKING) |
424 | cv_wait(&sme->sme_condvar, &sme->sme_mtx); |
425 | |
426 | DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n" , |
427 | __func__, see->see_pes.pes_dvname, sensor, type)); |
428 | |
429 | sme_remove_event(see, sme); |
430 | |
431 | if (LIST_EMPTY(&sme->sme_events_list)) { |
432 | sme_events_halt_callout(sme); |
433 | destroy = true; |
434 | } |
435 | mutex_exit(&sme->sme_mtx); |
436 | |
437 | if (destroy) |
438 | sme_events_destroy(sme); |
439 | |
440 | return 0; |
441 | } |
442 | |
443 | /* |
444 | * sme_event_unregister_sensor: |
445 | * |
446 | * + Unregisters any event associated with a specific sensor |
447 | * The caller must already own the sme_mtx. |
448 | */ |
449 | int |
450 | sme_event_unregister_sensor(struct sysmon_envsys *sme, envsys_data_t *edata) |
451 | { |
452 | sme_event_t *see; |
453 | bool found = false; |
454 | |
455 | KASSERT(mutex_owned(&sme->sme_mtx)); |
456 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
457 | if (see->see_edata == edata) { |
458 | found = true; |
459 | break; |
460 | } |
461 | } |
462 | if (!found) |
463 | return EINVAL; |
464 | |
465 | /* |
466 | * Wait for the event to finish its work, remove it from the list |
467 | * and release resources. |
468 | */ |
469 | while (see->see_flags & SEE_EVENT_WORKING) |
470 | cv_wait(&sme->sme_condvar, &sme->sme_mtx); |
471 | |
472 | DPRINTF(("%s: removed dev=%s sensor=%s\n" , |
473 | __func__, see->see_pes.pes_dvname, edata->desc)); |
474 | |
475 | sme_remove_event(see, sme); |
476 | |
477 | return 0; |
478 | } |
479 | |
480 | static void |
481 | sme_remove_event(sme_event_t *see, struct sysmon_envsys *sme) |
482 | { |
483 | |
484 | KASSERT(mutex_owned(&sme->sme_mtx)); |
485 | |
486 | if (see->see_edata->flags & ENVSYS_FHAS_ENTROPY) |
487 | rnd_detach_source(&see->see_edata->rnd_src); |
488 | LIST_REMOVE(see, see_list); |
489 | kmem_free(see, sizeof(*see)); |
490 | } |
491 | |
492 | /* |
493 | * sme_event_drvadd: |
494 | * |
495 | * + Registers a new event for a device that had enabled any of |
496 | * the monitoring flags in the driver. |
497 | */ |
498 | void |
499 | sme_event_drvadd(void *arg) |
500 | { |
501 | sme_event_drv_t *sed_t = arg; |
502 | sysmon_envsys_lim_t lims; |
503 | uint32_t props; |
504 | int error = 0; |
505 | const struct ev_reg_t *reg; |
506 | |
507 | KASSERT(sed_t != NULL); |
508 | |
509 | /* |
510 | * If driver provides a method to retrieve its internal limit |
511 | * values, call it and use those returned values as initial |
512 | * limits for event monitoring. |
513 | */ |
514 | props = 0; |
515 | if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS) |
516 | if (sed_t->sed_sme->sme_get_limits) |
517 | (*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme, |
518 | sed_t->sed_edata, |
519 | &lims, &props); |
520 | /* |
521 | * If driver doesn't provide a way to "absorb" user-specified |
522 | * limit values, we must monitor all limits ourselves |
523 | */ |
524 | if (sed_t->sed_sme->sme_set_limits == NULL) |
525 | props &= ~PROP_DRIVER_LIMITS; |
526 | |
527 | /* Register the events that were specified */ |
528 | |
529 | for (reg = reg_events; reg->name != NULL; reg++) { |
530 | if (sed_t->sed_edata->flags & reg->crittype) { |
531 | |
532 | error = sme_event_register(sed_t->sed_sdict, |
533 | sed_t->sed_edata, |
534 | sed_t->sed_sme, |
535 | &lims, props, |
536 | reg->powertype, |
537 | sed_t->sed_powertype); |
538 | if (error && error != EEXIST) |
539 | printf("%s: failed to add event! " |
540 | "error=%d sensor=%s event=%s\n" , |
541 | __func__, error, |
542 | sed_t->sed_edata->desc, reg->name); |
543 | else { |
544 | char str[ENVSYS_DESCLEN] = "monitoring-state-" ; |
545 | (void)strlcat(str, reg->name, sizeof(str)); |
546 | prop_dictionary_set_bool(sed_t->sed_sdict, |
547 | str, true); |
548 | } |
549 | } |
550 | } |
551 | |
552 | /* |
553 | * we are done, free memory now. |
554 | */ |
555 | kmem_free(sed_t, sizeof(*sed_t)); |
556 | } |
557 | |
558 | /* |
559 | * sme_events_init: |
560 | * |
561 | * + Initialize the events framework for this device. |
562 | */ |
563 | int |
564 | sme_events_init(struct sysmon_envsys *sme) |
565 | { |
566 | int error = 0; |
567 | |
568 | KASSERT(sme != NULL); |
569 | KASSERT(mutex_owned(&sme->sme_mtx)); |
570 | |
571 | error = workqueue_create(&sme->sme_wq, sme->sme_name, |
572 | sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE); |
573 | if (error) |
574 | return error; |
575 | |
576 | callout_init(&sme->sme_callout, CALLOUT_MPSAFE); |
577 | callout_setfunc(&sme->sme_callout, sme_events_check, sme); |
578 | sme->sme_flags |= SME_CALLOUT_INITIALIZED; |
579 | sme_schedule_callout(sme); |
580 | DPRINTF(("%s: events framework initialized for '%s'\n" , |
581 | __func__, sme->sme_name)); |
582 | |
583 | return error; |
584 | } |
585 | |
586 | /* |
587 | * sme_schedule_callout |
588 | * |
589 | * (Re)-schedule the device's callout timer |
590 | */ |
591 | void |
592 | sme_schedule_callout(struct sysmon_envsys *sme) |
593 | { |
594 | uint64_t timo; |
595 | |
596 | KASSERT(sme != NULL); |
597 | |
598 | if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) |
599 | return; |
600 | |
601 | if (sme->sme_events_timeout) |
602 | timo = sme->sme_events_timeout * hz; |
603 | else |
604 | timo = SME_EVTIMO; |
605 | |
606 | callout_stop(&sme->sme_callout); |
607 | callout_schedule(&sme->sme_callout, timo); |
608 | } |
609 | |
610 | /* |
611 | * sme_events_halt_callout: |
612 | * |
613 | * + Halt the callout of the event framework for this device. |
614 | */ |
615 | void |
616 | sme_events_halt_callout(struct sysmon_envsys *sme) |
617 | { |
618 | KASSERT(mutex_owned(&sme->sme_mtx)); |
619 | |
620 | /* |
621 | * Unset before callout_halt to ensure callout is not scheduled again |
622 | * during callout_halt. |
623 | */ |
624 | sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; |
625 | |
626 | callout_halt(&sme->sme_callout, &sme->sme_mtx); |
627 | } |
628 | |
629 | /* |
630 | * sme_events_destroy: |
631 | * |
632 | * + Destroy the callout and the workqueue of the event framework |
633 | * for this device. |
634 | */ |
635 | void |
636 | sme_events_destroy(struct sysmon_envsys *sme) |
637 | { |
638 | KASSERT(!mutex_owned(&sme->sme_mtx)); |
639 | KASSERT((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0); |
640 | |
641 | callout_destroy(&sme->sme_callout); |
642 | workqueue_destroy(sme->sme_wq); |
643 | |
644 | DPRINTF(("%s: events framework destroyed for '%s'\n" , |
645 | __func__, sme->sme_name)); |
646 | } |
647 | |
648 | /* |
649 | * sysmon_envsys_update_limits |
650 | * |
651 | * + If a driver needs to update the limits that it is providing, |
652 | * we need to update the dictionary data as well as the limits. |
653 | * This only makes sense if the driver is capable of providing |
654 | * its limits, and if there is a limits event-monitor. |
655 | */ |
656 | int |
657 | sysmon_envsys_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata) |
658 | { |
659 | int err; |
660 | |
661 | sysmon_envsys_acquire(sme, false); |
662 | if (sme->sme_get_limits == NULL || |
663 | (edata->flags & ENVSYS_FMONLIMITS) == 0) |
664 | err = EINVAL; |
665 | else |
666 | err = sme_update_limits(sme, edata); |
667 | sysmon_envsys_release(sme, false); |
668 | |
669 | return err; |
670 | } |
671 | |
672 | /* |
673 | * sme_update_limits |
674 | * |
675 | * + Internal version of sysmon_envsys_update_limits() to be used |
676 | * when the device has already been sysmon_envsys_acquire()d. |
677 | */ |
678 | |
679 | int |
680 | sme_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata) |
681 | { |
682 | prop_dictionary_t sdict = NULL; |
683 | prop_array_t array = NULL; |
684 | sysmon_envsys_lim_t lims; |
685 | sme_event_t *see; |
686 | uint32_t props = 0; |
687 | |
688 | /* Find the dictionary for this sensor */ |
689 | array = prop_dictionary_get(sme_propd, sme->sme_name); |
690 | if (array == NULL || |
691 | prop_object_type(array) != PROP_TYPE_ARRAY) { |
692 | DPRINTF(("%s: array device failed\n" , __func__)); |
693 | return EINVAL; |
694 | } |
695 | |
696 | sdict = prop_array_get(array, edata->sensor); |
697 | if (sdict == NULL) { |
698 | return EINVAL; |
699 | } |
700 | |
701 | /* Find the event definition to get its powertype */ |
702 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
703 | if (edata == see->see_edata && |
704 | see->see_type == PENVSYS_EVENT_LIMITS) |
705 | break; |
706 | } |
707 | if (see == NULL) |
708 | return EINVAL; |
709 | |
710 | /* Update limit values from driver if possible */ |
711 | if (sme->sme_get_limits != NULL) |
712 | (*sme->sme_get_limits)(sme, edata, &lims, &props); |
713 | |
714 | /* Update event and dictionary */ |
715 | sme_event_register(sdict, edata, sme, &lims, props, |
716 | PENVSYS_EVENT_LIMITS, see->see_pes.pes_type); |
717 | |
718 | return 0; |
719 | } |
720 | |
721 | /* |
722 | * sme_events_check: |
723 | * |
724 | * + Passes the events to the workqueue thread and stops |
725 | * the callout if the 'low-power' condition is triggered. |
726 | */ |
727 | void |
728 | sme_events_check(void *arg) |
729 | { |
730 | struct sysmon_envsys *sme = arg; |
731 | sme_event_t *see; |
732 | |
733 | KASSERT(sme != NULL); |
734 | |
735 | mutex_enter(&sme->sme_work_mtx); |
736 | if (sme->sme_busy > 0) { |
737 | log(LOG_WARNING, "%s: workqueue busy: updates stopped\n" , |
738 | sme->sme_name); |
739 | mutex_exit(&sme->sme_work_mtx); |
740 | return; |
741 | } |
742 | if (!mutex_tryenter(&sme->sme_mtx)) { |
743 | /* can't get lock - try again later */ |
744 | if (!sysmon_low_power) |
745 | sme_schedule_callout(sme); |
746 | mutex_exit(&sme->sme_work_mtx); |
747 | return; |
748 | } |
749 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
750 | workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); |
751 | see->see_edata->flags |= ENVSYS_FNEED_REFRESH; |
752 | sme->sme_busy++; |
753 | } |
754 | if (!sysmon_low_power) |
755 | sme_schedule_callout(sme); |
756 | mutex_exit(&sme->sme_work_mtx); |
757 | mutex_exit(&sme->sme_mtx); |
758 | } |
759 | |
760 | /* |
761 | * sme_events_worker: |
762 | * |
763 | * + workqueue thread that checks if there's a critical condition |
764 | * and sends an event if it was triggered. |
765 | */ |
766 | void |
767 | sme_events_worker(struct work *wk, void *arg) |
768 | { |
769 | sme_event_t *see = (void *)wk; |
770 | struct sysmon_envsys *sme = see->see_sme; |
771 | envsys_data_t *edata = see->see_edata; |
772 | |
773 | KASSERT(wk == &see->see_wk); |
774 | KASSERT(sme != NULL); |
775 | KASSERT(edata != NULL); |
776 | |
777 | mutex_enter(&sme->sme_mtx); |
778 | see->see_flags |= SEE_EVENT_WORKING; |
779 | /* |
780 | * sme_events_check marks the sensors to make us refresh them here. |
781 | * sme_envsys_refresh_sensor will not call the driver if the driver |
782 | * does its own setting of the sensor value. |
783 | */ |
784 | if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) { |
785 | /* refresh sensor in device */ |
786 | sysmon_envsys_refresh_sensor(sme, edata); |
787 | edata->flags &= ~ENVSYS_FNEED_REFRESH; |
788 | } |
789 | |
790 | DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d " |
791 | "value_cur=%d upropset=0x%04x\n" , __func__, sme->sme_name, edata->desc, |
792 | edata->sensor, see->see_type, edata->state, edata->units, |
793 | edata->value_cur, edata->upropset)); |
794 | |
795 | /* skip the event if current sensor is in invalid state */ |
796 | if (edata->state == ENVSYS_SINVALID) |
797 | goto out; |
798 | |
799 | /* |
800 | * For range limits, if the driver claims responsibility for |
801 | * limit/range checking, just user driver-supplied status. |
802 | * Else calculate our own status. Note that driver must |
803 | * relinquish responsibility for ALL limits if there is even |
804 | * one limit that it cannot handle! |
805 | * |
806 | * If this is a CAPACITY monitor, but the sensor's max_value |
807 | * is not set, treat it as though the monitor does not exist. |
808 | */ |
809 | if ((see->see_type == PENVSYS_EVENT_LIMITS || |
810 | see->see_type == PENVSYS_EVENT_CAPACITY) && |
811 | (edata->upropset & PROP_DRIVER_LIMITS) == 0) { |
812 | if ((see->see_type == PENVSYS_EVENT_CAPACITY) && |
813 | (edata->value_max == 0)) |
814 | edata->state = ENVSYS_SVALID; |
815 | else if ((edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) && |
816 | (edata->value_cur < edata->limits.sel_critmin)) |
817 | edata->state = ENVSYS_SCRITUNDER; |
818 | else if ((edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) && |
819 | (edata->value_cur < edata->limits.sel_warnmin)) |
820 | edata->state = ENVSYS_SWARNUNDER; |
821 | else if ((edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) && |
822 | (edata->value_cur > edata->limits.sel_critmax)) |
823 | edata->state = ENVSYS_SCRITOVER; |
824 | else if ((edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) && |
825 | (edata->value_cur > edata->limits.sel_warnmax)) |
826 | edata->state = ENVSYS_SWARNOVER; |
827 | else |
828 | edata->state = ENVSYS_SVALID; |
829 | } |
830 | sme_deliver_event(see); |
831 | |
832 | out: |
833 | see->see_flags &= ~SEE_EVENT_WORKING; |
834 | cv_broadcast(&sme->sme_condvar); |
835 | mutex_enter(&sme->sme_work_mtx); |
836 | KASSERT(sme->sme_busy > 0); |
837 | sme->sme_busy--; |
838 | mutex_exit(&sme->sme_work_mtx); |
839 | mutex_exit(&sme->sme_mtx); |
840 | } |
841 | |
842 | /* |
843 | * sysmon_envsys_sensor_event |
844 | * |
845 | * + Find the monitor event of a particular type for a given sensor |
846 | * on a device and deliver the event if one is required. If |
847 | * no event type is specified, deliver all events for the sensor. |
848 | */ |
849 | void |
850 | sysmon_envsys_sensor_event(struct sysmon_envsys *sme, envsys_data_t *edata, |
851 | int ev_type) |
852 | { |
853 | sme_event_t *see; |
854 | |
855 | mutex_enter(&sme->sme_mtx); |
856 | LIST_FOREACH(see, &sme->sme_events_list, see_list) { |
857 | if (edata != see->see_edata) |
858 | continue; |
859 | if (ev_type == 0 || |
860 | ev_type == see->see_type) { |
861 | sme_deliver_event(see); |
862 | if (ev_type != 0) |
863 | break; |
864 | } |
865 | } |
866 | mutex_exit(&sme->sme_mtx); |
867 | } |
868 | |
869 | /* |
870 | * sme_deliver_event: |
871 | * |
872 | * + If new sensor state requires it, send an event to powerd |
873 | * |
874 | * Must be called with the device's sysmon mutex held |
875 | * see->see_sme->sme_mtx |
876 | */ |
877 | void |
878 | sme_deliver_event(sme_event_t *see) |
879 | { |
880 | envsys_data_t *edata = see->see_edata; |
881 | const struct sme_descr_entry *sdt = NULL; |
882 | const struct sme_sensor_event *sse = sme_sensor_event; |
883 | int i, state = 0; |
884 | |
885 | switch (see->see_type) { |
886 | case PENVSYS_EVENT_LIMITS: |
887 | case PENVSYS_EVENT_CAPACITY: |
888 | /* |
889 | * Send event if state has changed |
890 | */ |
891 | if (edata->state == see->see_evstate) |
892 | break; |
893 | |
894 | for (i = 0; sse[i].state != -1; i++) |
895 | if (sse[i].state == edata->state) |
896 | break; |
897 | |
898 | if (sse[i].state == -1) |
899 | break; |
900 | |
901 | if (edata->state == ENVSYS_SVALID) |
902 | sysmon_penvsys_event(&see->see_pes, |
903 | PENVSYS_EVENT_NORMAL); |
904 | else |
905 | sysmon_penvsys_event(&see->see_pes, sse[i].event); |
906 | |
907 | see->see_evstate = edata->state; |
908 | DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n" , |
909 | __func__, see->see_sme->sme_name, edata->desc, |
910 | edata->sensor, edata->state, |
911 | (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL : |
912 | sse[i].event)); |
913 | |
914 | break; |
915 | |
916 | /* |
917 | * Send PENVSYS_EVENT_CRITICAL event if: |
918 | * State has gone from non-CRITICAL to CRITICAL, |
919 | * State remains CRITICAL and value has changed, or |
920 | * State has returned from CRITICAL to non-CRITICAL |
921 | */ |
922 | case PENVSYS_EVENT_CRITICAL: |
923 | DPRINTF(("%s: CRITICAL: old/new state %d/%d, old/new value " |
924 | "%d/%d\n" , __func__, see->see_evstate, edata->state, |
925 | see->see_evvalue, edata->value_cur)); |
926 | if (edata->state == ENVSYS_SVALID && |
927 | see->see_evstate != ENVSYS_SVALID) { |
928 | sysmon_penvsys_event(&see->see_pes, |
929 | PENVSYS_EVENT_NORMAL); |
930 | see->see_evstate = ENVSYS_SVALID; |
931 | break; |
932 | } else if (edata->state != ENVSYS_SCRITICAL) |
933 | break; |
934 | if (see->see_evstate != ENVSYS_SCRITICAL || |
935 | see->see_evvalue != edata->value_cur) { |
936 | sysmon_penvsys_event(&see->see_pes, |
937 | PENVSYS_EVENT_CRITICAL); |
938 | see->see_evstate = ENVSYS_SCRITICAL; |
939 | } |
940 | see->see_evvalue = edata->value_cur; |
941 | break; |
942 | |
943 | /* |
944 | * if value_cur is not normal (battery) or online (drive), |
945 | * send the event... |
946 | */ |
947 | case PENVSYS_EVENT_STATE_CHANGED: |
948 | /* |
949 | * the state has not been changed, just ignore the event. |
950 | */ |
951 | if (edata->value_cur == see->see_evvalue) |
952 | break; |
953 | |
954 | switch (edata->units) { |
955 | case ENVSYS_DRIVE: |
956 | sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES, |
957 | edata->value_cur); |
958 | state = ENVSYS_DRIVE_ONLINE; |
959 | break; |
960 | case ENVSYS_BATTERY_CAPACITY: |
961 | sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY, |
962 | edata->value_cur); |
963 | state = ENVSYS_BATTERY_CAPACITY_NORMAL; |
964 | break; |
965 | case ENVSYS_INDICATOR: |
966 | sdt = sme_find_table_entry(SME_DESC_INDICATOR, |
967 | edata->value_cur); |
968 | state = see->see_evvalue; /* force state change */ |
969 | break; |
970 | default: |
971 | panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED" , |
972 | __func__); |
973 | } |
974 | |
975 | if (sdt->type == -1) |
976 | break; |
977 | |
978 | /* |
979 | * copy current state description. |
980 | */ |
981 | (void)strlcpy(see->see_pes.pes_statedesc, sdt->desc, |
982 | sizeof(see->see_pes.pes_statedesc)); |
983 | |
984 | if (edata->value_cur == state) |
985 | /* |
986 | * state returned to normal condition |
987 | */ |
988 | sysmon_penvsys_event(&see->see_pes, |
989 | PENVSYS_EVENT_NORMAL); |
990 | else |
991 | /* |
992 | * state changed to abnormal condition |
993 | */ |
994 | sysmon_penvsys_event(&see->see_pes, see->see_type); |
995 | |
996 | see->see_evvalue = edata->value_cur; |
997 | |
998 | /* |
999 | * There's no need to continue if it's a drive sensor. |
1000 | */ |
1001 | if (edata->units == ENVSYS_DRIVE) |
1002 | break; |
1003 | |
1004 | /* |
1005 | * Check if the system is running in low power and send the |
1006 | * event to powerd (if running) or shutdown the system |
1007 | * otherwise. |
1008 | */ |
1009 | if (!sysmon_low_power && sme_event_check_low_power()) { |
1010 | struct penvsys_state pes; |
1011 | |
1012 | /* |
1013 | * Stop the callout and send the 'low-power' event. |
1014 | */ |
1015 | sysmon_low_power = true; |
1016 | callout_stop(&see->see_sme->sme_callout); |
1017 | pes.pes_type = PENVSYS_TYPE_BATTERY; |
1018 | sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); |
1019 | } |
1020 | break; |
1021 | case PENVSYS_EVENT_NULL: |
1022 | break; |
1023 | default: |
1024 | panic("%s: invalid event type %d" , __func__, see->see_type); |
1025 | } |
1026 | } |
1027 | |
1028 | /* |
1029 | * Returns true if the system is in low power state: an AC adapter |
1030 | * is OFF and all batteries are in LOW/CRITICAL state. |
1031 | */ |
1032 | static bool |
1033 | sme_event_check_low_power(void) |
1034 | { |
1035 | if (!sme_acadapter_check()) |
1036 | return false; |
1037 | |
1038 | return sme_battery_check(); |
1039 | } |
1040 | |
1041 | /* |
1042 | * Called with the sysmon_envsys device mtx held through the |
1043 | * workqueue thread. |
1044 | */ |
1045 | static bool |
1046 | sme_acadapter_check(void) |
1047 | { |
1048 | struct sysmon_envsys *sme; |
1049 | envsys_data_t *edata; |
1050 | bool dev = false, sensor = false; |
1051 | |
1052 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1053 | if (sme->sme_class == SME_CLASS_ACADAPTER) { |
1054 | dev = true; |
1055 | break; |
1056 | } |
1057 | } |
1058 | |
1059 | /* |
1060 | * No AC Adapter devices were found. |
1061 | */ |
1062 | if (!dev) |
1063 | return false; |
1064 | |
1065 | /* |
1066 | * Check if there's an AC adapter device connected. |
1067 | */ |
1068 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1069 | if (edata->units == ENVSYS_INDICATOR) { |
1070 | sensor = true; |
1071 | /* refresh current sensor */ |
1072 | sysmon_envsys_refresh_sensor(sme, edata); |
1073 | |
1074 | if (edata->value_cur) |
1075 | return false; |
1076 | } |
1077 | } |
1078 | |
1079 | if (!sensor) |
1080 | return false; |
1081 | |
1082 | /* |
1083 | * AC adapter found and not connected. |
1084 | */ |
1085 | return true; |
1086 | } |
1087 | |
1088 | /* |
1089 | * Called with the sysmon_envsys device mtx held through the |
1090 | * workqueue thread. |
1091 | */ |
1092 | static bool |
1093 | sme_battery_check(void) |
1094 | { |
1095 | struct sysmon_envsys *sme; |
1096 | envsys_data_t *edata; |
1097 | int batteriesfound = 0; |
1098 | bool present, batterycap, batterycharge; |
1099 | |
1100 | /* |
1101 | * Check for battery devices and its state. |
1102 | */ |
1103 | LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { |
1104 | if (sme->sme_class != SME_CLASS_BATTERY) |
1105 | continue; |
1106 | |
1107 | present = true; |
1108 | |
1109 | /* |
1110 | * XXX |
1111 | * this assumes that the first valid ENVSYS_INDICATOR is the |
1112 | * presence indicator |
1113 | */ |
1114 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1115 | if ((edata->units == ENVSYS_INDICATOR) && |
1116 | (edata->state == ENVSYS_SVALID)) { |
1117 | present = edata->value_cur; |
1118 | break; |
1119 | } |
1120 | } |
1121 | if (!present) |
1122 | continue; |
1123 | /* |
1124 | * We've found a battery device... |
1125 | */ |
1126 | batteriesfound++; |
1127 | batterycap = batterycharge = false; |
1128 | TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { |
1129 | /* no need to even look at sensors that aren't valid */ |
1130 | if (edata->state != ENVSYS_SVALID) |
1131 | continue; |
1132 | if (edata->units == ENVSYS_BATTERY_CAPACITY) { |
1133 | batterycap = true; |
1134 | if (!sme_battery_critical(edata)) |
1135 | return false; |
1136 | } else if (edata->units == ENVSYS_BATTERY_CHARGE) { |
1137 | batterycharge = true; |
1138 | if (edata->value_cur) |
1139 | return false; |
1140 | } |
1141 | } |
1142 | if (!batterycap || !batterycharge) |
1143 | return false; |
1144 | } |
1145 | |
1146 | if (!batteriesfound) |
1147 | return false; |
1148 | |
1149 | /* |
1150 | * All batteries in low/critical capacity and discharging. |
1151 | */ |
1152 | return true; |
1153 | } |
1154 | |
1155 | static bool |
1156 | sme_battery_critical(envsys_data_t *edata) |
1157 | { |
1158 | if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL || |
1159 | edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW) |
1160 | return true; |
1161 | |
1162 | return false; |
1163 | } |
1164 | |