1 | /* $NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2007 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) 2003 Wasabi Systems, Inc. |
30 | * All rights reserved. |
31 | * |
32 | * Written by Jason R. Thorpe for Wasabi Systems, Inc. |
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 for the NetBSD Project by |
45 | * Wasabi Systems, Inc. |
46 | * 4. The name of Wasabi Systems, Inc. may not be used to endorse |
47 | * or promote products derived from this software without specific prior |
48 | * written permission. |
49 | * |
50 | * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND |
51 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
52 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
53 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC |
54 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
55 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
56 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
57 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
58 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
59 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
60 | * POSSIBILITY OF SUCH DAMAGE. |
61 | */ |
62 | |
63 | /* |
64 | * Power management framework for sysmon. |
65 | * |
66 | * We defer to a power management daemon running in userspace, since |
67 | * power management is largely a policy issue. This merely provides |
68 | * for power management event notification to that daemon. |
69 | */ |
70 | |
71 | #include <sys/cdefs.h> |
72 | __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $" ); |
73 | |
74 | #ifdef _KERNEL_OPT |
75 | #include "opt_compat_netbsd.h" |
76 | #endif |
77 | |
78 | #include <sys/param.h> |
79 | #include <sys/reboot.h> |
80 | #include <sys/systm.h> |
81 | #include <sys/poll.h> |
82 | #include <sys/select.h> |
83 | #include <sys/vnode.h> |
84 | #include <sys/condvar.h> |
85 | #include <sys/mutex.h> |
86 | #include <sys/kmem.h> |
87 | #include <sys/proc.h> |
88 | #include <sys/device.h> |
89 | #include <sys/rndsource.h> |
90 | #include <sys/module.h> |
91 | #include <sys/once.h> |
92 | |
93 | #include <dev/sysmon/sysmonvar.h> |
94 | #include <prop/proplib.h> |
95 | |
96 | MODULE(MODULE_CLASS_DRIVER, sysmon_power, "sysmon" ); |
97 | |
98 | /* |
99 | * Singly linked list for dictionaries to be stored/sent. |
100 | */ |
101 | struct power_event_dictionary { |
102 | SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head; |
103 | prop_dictionary_t dict; |
104 | int flags; |
105 | }; |
106 | |
107 | struct power_event_description { |
108 | int type; |
109 | const char *desc; |
110 | }; |
111 | |
112 | /* |
113 | * Available events for power switches. |
114 | */ |
115 | static const struct power_event_description pswitch_event_desc[] = { |
116 | { PSWITCH_EVENT_PRESSED, "pressed" }, |
117 | { PSWITCH_EVENT_RELEASED, "released" }, |
118 | { -1, NULL } |
119 | }; |
120 | |
121 | /* |
122 | * Available script names for power switches. |
123 | */ |
124 | static const struct power_event_description pswitch_type_desc[] = { |
125 | { PSWITCH_TYPE_POWER, "power_button" }, |
126 | { PSWITCH_TYPE_SLEEP, "sleep_button" }, |
127 | { PSWITCH_TYPE_LID, "lid_switch" }, |
128 | { PSWITCH_TYPE_RESET, "reset_button" }, |
129 | { PSWITCH_TYPE_ACADAPTER, "acadapter" }, |
130 | { PSWITCH_TYPE_HOTKEY, "hotkey_button" }, |
131 | { PSWITCH_TYPE_RADIO, "radio_button" }, |
132 | { -1, NULL } |
133 | }; |
134 | |
135 | /* |
136 | * Available events for envsys(4). |
137 | */ |
138 | static const struct power_event_description penvsys_event_desc[] = { |
139 | { PENVSYS_EVENT_NORMAL, "normal" }, |
140 | { PENVSYS_EVENT_CRITICAL, "critical" }, |
141 | { PENVSYS_EVENT_CRITOVER, "critical-over" }, |
142 | { PENVSYS_EVENT_CRITUNDER, "critical-under" }, |
143 | { PENVSYS_EVENT_WARNOVER, "warning-over" }, |
144 | { PENVSYS_EVENT_WARNUNDER, "warning-under" }, |
145 | { PENVSYS_EVENT_BATT_CRIT, "critical-capacity" }, |
146 | { PENVSYS_EVENT_BATT_WARN, "warning-capacity" }, |
147 | { PENVSYS_EVENT_BATT_HIGH, "high-capacity" }, |
148 | { PENVSYS_EVENT_BATT_MAX, "maximum-capacity" }, |
149 | { PENVSYS_EVENT_STATE_CHANGED, "state-changed" }, |
150 | { PENVSYS_EVENT_LOW_POWER, "low-power" }, |
151 | { -1, NULL } |
152 | }; |
153 | |
154 | /* |
155 | * Available script names for envsys(4). |
156 | */ |
157 | static const struct power_event_description penvsys_type_desc[] = { |
158 | { PENVSYS_TYPE_BATTERY, "sensor_battery" }, |
159 | { PENVSYS_TYPE_DRIVE, "sensor_drive" }, |
160 | { PENVSYS_TYPE_FAN, "sensor_fan" }, |
161 | { PENVSYS_TYPE_INDICATOR, "sensor_indicator" }, |
162 | { PENVSYS_TYPE_POWER, "sensor_power" }, |
163 | { PENVSYS_TYPE_RESISTANCE, "sensor_resistance" }, |
164 | { PENVSYS_TYPE_TEMP, "sensor_temperature" }, |
165 | { PENVSYS_TYPE_VOLTAGE, "sensor_voltage" }, |
166 | { -1, NULL } |
167 | }; |
168 | |
169 | #define SYSMON_MAX_POWER_EVENTS 32 |
170 | #define SYSMON_POWER_DICTIONARY_BUSY 0x01 |
171 | #define SYSMON_POWER_DICTIONARY_READY 0x02 |
172 | |
173 | static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS]; |
174 | static int sysmon_power_event_queue_head; |
175 | static int sysmon_power_event_queue_tail; |
176 | static int sysmon_power_event_queue_count; |
177 | |
178 | static krndsource_t sysmon_rndsource; |
179 | |
180 | static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list = |
181 | SIMPLEQ_HEAD_INITIALIZER(pev_dict_list); |
182 | |
183 | static struct selinfo sysmon_power_event_queue_selinfo; |
184 | static struct lwp *sysmon_power_daemon; |
185 | |
186 | static kmutex_t sysmon_power_event_queue_mtx; |
187 | static kcondvar_t sysmon_power_event_queue_cv; |
188 | |
189 | static char sysmon_power_type[32]; |
190 | |
191 | static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int); |
192 | static int sysmon_power_daemon_task(struct power_event_dictionary *, |
193 | void *, int); |
194 | static void sysmon_power_destroy_dictionary(struct power_event_dictionary *); |
195 | |
196 | static struct sysmon_opvec sysmon_power_opvec = { |
197 | sysmonopen_power, sysmonclose_power, sysmonioctl_power, |
198 | sysmonread_power, sysmonpoll_power, sysmonkqfilter_power |
199 | }; |
200 | |
201 | #define SYSMON_NEXT_EVENT(x) (((x) + 1) % SYSMON_MAX_POWER_EVENTS) |
202 | |
203 | ONCE_DECL(once_power); |
204 | |
205 | static int |
206 | power_preinit(void) |
207 | { |
208 | |
209 | mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE); |
210 | cv_init(&sysmon_power_event_queue_cv, "smpower" ); |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | /* |
216 | * sysmon_power_init: |
217 | * |
218 | * Initializes the mutexes and condition variables in the |
219 | * boot process via module initialization process. |
220 | */ |
221 | int |
222 | sysmon_power_init(void) |
223 | { |
224 | int error; |
225 | |
226 | (void)RUN_ONCE(&once_power, power_preinit); |
227 | |
228 | selinit(&sysmon_power_event_queue_selinfo); |
229 | |
230 | rnd_attach_source(&sysmon_rndsource, "system-power" , |
231 | RND_TYPE_POWER, RND_FLAG_DEFAULT); |
232 | |
233 | error = sysmon_attach_minor(SYSMON_MINOR_POWER, &sysmon_power_opvec); |
234 | |
235 | return error; |
236 | } |
237 | |
238 | int |
239 | sysmon_power_fini(void) |
240 | { |
241 | int error; |
242 | |
243 | if (sysmon_power_daemon != NULL) |
244 | error = EBUSY; |
245 | else |
246 | error = sysmon_attach_minor(SYSMON_MINOR_POWER, NULL); |
247 | |
248 | if (error == 0) { |
249 | rnd_detach_source(&sysmon_rndsource); |
250 | seldestroy(&sysmon_power_event_queue_selinfo); |
251 | cv_destroy(&sysmon_power_event_queue_cv); |
252 | mutex_destroy(&sysmon_power_event_queue_mtx); |
253 | } |
254 | |
255 | return error; |
256 | } |
257 | |
258 | /* |
259 | * sysmon_queue_power_event: |
260 | * |
261 | * Enqueue a power event for the power management daemon. Returns |
262 | * non-zero if we were able to enqueue a power event. |
263 | */ |
264 | static int |
265 | sysmon_queue_power_event(power_event_t *pev) |
266 | { |
267 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
268 | |
269 | if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS) |
270 | return 0; |
271 | |
272 | sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev; |
273 | sysmon_power_event_queue_head = |
274 | SYSMON_NEXT_EVENT(sysmon_power_event_queue_head); |
275 | sysmon_power_event_queue_count++; |
276 | |
277 | return 1; |
278 | } |
279 | |
280 | /* |
281 | * sysmon_get_power_event: |
282 | * |
283 | * Get a power event from the queue. Returns non-zero if there |
284 | * is an event available. |
285 | */ |
286 | static int |
287 | sysmon_get_power_event(power_event_t *pev) |
288 | { |
289 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
290 | |
291 | if (sysmon_power_event_queue_count == 0) |
292 | return 0; |
293 | |
294 | *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail]; |
295 | sysmon_power_event_queue_tail = |
296 | SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail); |
297 | sysmon_power_event_queue_count--; |
298 | |
299 | return 1; |
300 | } |
301 | |
302 | /* |
303 | * sysmon_power_event_queue_flush: |
304 | * |
305 | * Flush the event queue, and reset all state. |
306 | */ |
307 | static void |
308 | sysmon_power_event_queue_flush(void) |
309 | { |
310 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
311 | |
312 | sysmon_power_event_queue_head = 0; |
313 | sysmon_power_event_queue_tail = 0; |
314 | sysmon_power_event_queue_count = 0; |
315 | } |
316 | |
317 | /* |
318 | * sysmon_power_daemon_task: |
319 | * |
320 | * Assign required power event members and sends a signal |
321 | * to the process to notify that an event was enqueued successfully. |
322 | */ |
323 | static int |
324 | sysmon_power_daemon_task(struct power_event_dictionary *ped, |
325 | void *pev_data, int event) |
326 | { |
327 | power_event_t pev; |
328 | int rv, error = 0; |
329 | |
330 | if (!ped || !ped->dict || !pev_data) |
331 | return EINVAL; |
332 | |
333 | mutex_enter(&sysmon_power_event_queue_mtx); |
334 | |
335 | switch (event) { |
336 | /* |
337 | * Power switch events. |
338 | */ |
339 | case PSWITCH_EVENT_PRESSED: |
340 | case PSWITCH_EVENT_RELEASED: |
341 | { |
342 | |
343 | struct sysmon_pswitch *pswitch = |
344 | (struct sysmon_pswitch *)pev_data; |
345 | |
346 | pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE; |
347 | #ifdef COMPAT_40 |
348 | pev.pev_switch.psws_state = event; |
349 | pev.pev_switch.psws_type = pswitch->smpsw_type; |
350 | |
351 | if (pswitch->smpsw_name) { |
352 | (void)strlcpy(pev.pev_switch.psws_name, |
353 | pswitch->smpsw_name, |
354 | sizeof(pev.pev_switch.psws_name)); |
355 | } |
356 | #endif |
357 | error = sysmon_power_make_dictionary(ped->dict, |
358 | pswitch, |
359 | event, |
360 | pev.pev_type); |
361 | if (error) { |
362 | mutex_exit(&sysmon_power_event_queue_mtx); |
363 | goto out; |
364 | } |
365 | |
366 | break; |
367 | } |
368 | |
369 | /* |
370 | * ENVSYS events. |
371 | */ |
372 | case PENVSYS_EVENT_NORMAL: |
373 | case PENVSYS_EVENT_CRITICAL: |
374 | case PENVSYS_EVENT_CRITUNDER: |
375 | case PENVSYS_EVENT_CRITOVER: |
376 | case PENVSYS_EVENT_WARNUNDER: |
377 | case PENVSYS_EVENT_WARNOVER: |
378 | case PENVSYS_EVENT_BATT_CRIT: |
379 | case PENVSYS_EVENT_BATT_WARN: |
380 | case PENVSYS_EVENT_BATT_HIGH: |
381 | case PENVSYS_EVENT_BATT_MAX: |
382 | case PENVSYS_EVENT_STATE_CHANGED: |
383 | case PENVSYS_EVENT_LOW_POWER: |
384 | { |
385 | struct penvsys_state *penvsys = |
386 | (struct penvsys_state *)pev_data; |
387 | |
388 | pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE; |
389 | |
390 | error = sysmon_power_make_dictionary(ped->dict, |
391 | penvsys, |
392 | event, |
393 | pev.pev_type); |
394 | if (error) { |
395 | mutex_exit(&sysmon_power_event_queue_mtx); |
396 | goto out; |
397 | } |
398 | |
399 | break; |
400 | } |
401 | default: |
402 | error = ENOTTY; |
403 | mutex_exit(&sysmon_power_event_queue_mtx); |
404 | goto out; |
405 | } |
406 | |
407 | /* |
408 | * Enqueue the event. |
409 | */ |
410 | rv = sysmon_queue_power_event(&pev); |
411 | if (rv == 0) { |
412 | printf("%s: WARNING: state change event %d lost; " |
413 | "queue full\n" , __func__, pev.pev_type); |
414 | mutex_exit(&sysmon_power_event_queue_mtx); |
415 | error = EINVAL; |
416 | goto out; |
417 | } else { |
418 | /* |
419 | * Notify the daemon that an event is ready and its |
420 | * dictionary is ready to be fetched. |
421 | */ |
422 | ped->flags |= SYSMON_POWER_DICTIONARY_READY; |
423 | SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head); |
424 | cv_broadcast(&sysmon_power_event_queue_cv); |
425 | mutex_exit(&sysmon_power_event_queue_mtx); |
426 | selnotify(&sysmon_power_event_queue_selinfo, 0, 0); |
427 | } |
428 | |
429 | out: |
430 | return error; |
431 | } |
432 | |
433 | /* |
434 | * sysmonopen_power: |
435 | * |
436 | * Open the system monitor device. |
437 | */ |
438 | int |
439 | sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l) |
440 | { |
441 | int error = 0; |
442 | |
443 | mutex_enter(&sysmon_power_event_queue_mtx); |
444 | if (sysmon_power_daemon != NULL) |
445 | error = EBUSY; |
446 | else { |
447 | sysmon_power_daemon = l; |
448 | sysmon_power_event_queue_flush(); |
449 | } |
450 | mutex_exit(&sysmon_power_event_queue_mtx); |
451 | |
452 | return error; |
453 | } |
454 | |
455 | /* |
456 | * sysmonclose_power: |
457 | * |
458 | * Close the system monitor device. |
459 | */ |
460 | int |
461 | sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l) |
462 | { |
463 | int count; |
464 | |
465 | mutex_enter(&sysmon_power_event_queue_mtx); |
466 | count = sysmon_power_event_queue_count; |
467 | sysmon_power_daemon = NULL; |
468 | sysmon_power_event_queue_flush(); |
469 | mutex_exit(&sysmon_power_event_queue_mtx); |
470 | |
471 | if (count) |
472 | printf("WARNING: %d power event%s lost by exiting daemon\n" , |
473 | count, count > 1 ? "s" : "" ); |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | /* |
479 | * sysmonread_power: |
480 | * |
481 | * Read the system monitor device. |
482 | */ |
483 | int |
484 | sysmonread_power(dev_t dev, struct uio *uio, int flags) |
485 | { |
486 | power_event_t pev; |
487 | int rv; |
488 | |
489 | /* We only allow one event to be read at a time. */ |
490 | if (uio->uio_resid != POWER_EVENT_MSG_SIZE) |
491 | return EINVAL; |
492 | |
493 | mutex_enter(&sysmon_power_event_queue_mtx); |
494 | for (;;) { |
495 | if (sysmon_get_power_event(&pev)) { |
496 | rv = uiomove(&pev, POWER_EVENT_MSG_SIZE, uio); |
497 | break; |
498 | } |
499 | |
500 | if (flags & IO_NDELAY) { |
501 | rv = EWOULDBLOCK; |
502 | break; |
503 | } |
504 | |
505 | cv_wait(&sysmon_power_event_queue_cv, |
506 | &sysmon_power_event_queue_mtx); |
507 | } |
508 | mutex_exit(&sysmon_power_event_queue_mtx); |
509 | |
510 | return rv; |
511 | } |
512 | |
513 | /* |
514 | * sysmonpoll_power: |
515 | * |
516 | * Poll the system monitor device. |
517 | */ |
518 | int |
519 | sysmonpoll_power(dev_t dev, int events, struct lwp *l) |
520 | { |
521 | int revents; |
522 | |
523 | revents = events & (POLLOUT | POLLWRNORM); |
524 | |
525 | /* Attempt to save some work. */ |
526 | if ((events & (POLLIN | POLLRDNORM)) == 0) |
527 | return revents; |
528 | |
529 | mutex_enter(&sysmon_power_event_queue_mtx); |
530 | if (sysmon_power_event_queue_count) |
531 | revents |= events & (POLLIN | POLLRDNORM); |
532 | else |
533 | selrecord(l, &sysmon_power_event_queue_selinfo); |
534 | mutex_exit(&sysmon_power_event_queue_mtx); |
535 | |
536 | return revents; |
537 | } |
538 | |
539 | static void |
540 | filt_sysmon_power_rdetach(struct knote *kn) |
541 | { |
542 | |
543 | mutex_enter(&sysmon_power_event_queue_mtx); |
544 | SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist, |
545 | kn, knote, kn_selnext); |
546 | mutex_exit(&sysmon_power_event_queue_mtx); |
547 | } |
548 | |
549 | static int |
550 | filt_sysmon_power_read(struct knote *kn, long hint) |
551 | { |
552 | |
553 | mutex_enter(&sysmon_power_event_queue_mtx); |
554 | kn->kn_data = sysmon_power_event_queue_count; |
555 | mutex_exit(&sysmon_power_event_queue_mtx); |
556 | |
557 | return kn->kn_data > 0; |
558 | } |
559 | |
560 | static const struct filterops sysmon_power_read_filtops = |
561 | { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read }; |
562 | |
563 | static const struct filterops sysmon_power_write_filtops = |
564 | { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue }; |
565 | |
566 | /* |
567 | * sysmonkqfilter_power: |
568 | * |
569 | * Kqueue filter for the system monitor device. |
570 | */ |
571 | int |
572 | sysmonkqfilter_power(dev_t dev, struct knote *kn) |
573 | { |
574 | struct klist *klist; |
575 | |
576 | switch (kn->kn_filter) { |
577 | case EVFILT_READ: |
578 | klist = &sysmon_power_event_queue_selinfo.sel_klist; |
579 | kn->kn_fop = &sysmon_power_read_filtops; |
580 | break; |
581 | |
582 | case EVFILT_WRITE: |
583 | klist = &sysmon_power_event_queue_selinfo.sel_klist; |
584 | kn->kn_fop = &sysmon_power_write_filtops; |
585 | break; |
586 | |
587 | default: |
588 | return EINVAL; |
589 | } |
590 | |
591 | mutex_enter(&sysmon_power_event_queue_mtx); |
592 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
593 | mutex_exit(&sysmon_power_event_queue_mtx); |
594 | |
595 | return 0; |
596 | } |
597 | |
598 | /* |
599 | * sysmonioctl_power: |
600 | * |
601 | * Perform a power management control request. |
602 | */ |
603 | int |
604 | sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
605 | { |
606 | int error = 0; |
607 | |
608 | switch (cmd) { |
609 | case POWER_IOC_GET_TYPE: |
610 | case POWER_IOC_GET_TYPE_WITH_LOSSAGE: |
611 | { |
612 | struct power_type *power_type = (void *) data; |
613 | |
614 | (void)strlcpy(power_type->power_type, |
615 | sysmon_power_type, |
616 | sizeof(power_type->power_type)); |
617 | break; |
618 | } |
619 | case POWER_EVENT_RECVDICT: |
620 | { |
621 | struct plistref *plist = (struct plistref *)data; |
622 | struct power_event_dictionary *ped; |
623 | |
624 | /* |
625 | * Get the first dictionary enqueued and mark it |
626 | * as busy. |
627 | */ |
628 | mutex_enter(&sysmon_power_event_queue_mtx); |
629 | ped = SIMPLEQ_FIRST(&pev_dict_list); |
630 | if (!ped || !ped->dict) { |
631 | mutex_exit(&sysmon_power_event_queue_mtx); |
632 | error = ENOTSUP; |
633 | break; |
634 | } |
635 | |
636 | if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) { |
637 | mutex_exit(&sysmon_power_event_queue_mtx); |
638 | error = EINVAL; |
639 | break; |
640 | } |
641 | |
642 | if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) { |
643 | mutex_exit(&sysmon_power_event_queue_mtx); |
644 | error = EBUSY; |
645 | break; |
646 | } |
647 | |
648 | ped->flags |= SYSMON_POWER_DICTIONARY_BUSY; |
649 | mutex_exit(&sysmon_power_event_queue_mtx); |
650 | |
651 | /* |
652 | * Send it now. |
653 | */ |
654 | error = prop_dictionary_copyout_ioctl(plist, |
655 | cmd, |
656 | ped->dict); |
657 | |
658 | /* |
659 | * Remove the dictionary now that we don't need it. |
660 | */ |
661 | mutex_enter(&sysmon_power_event_queue_mtx); |
662 | ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY; |
663 | ped->flags &= ~SYSMON_POWER_DICTIONARY_READY; |
664 | SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head); |
665 | mutex_exit(&sysmon_power_event_queue_mtx); |
666 | sysmon_power_destroy_dictionary(ped); |
667 | |
668 | break; |
669 | } |
670 | default: |
671 | error = ENOTTY; |
672 | } |
673 | |
674 | return error; |
675 | } |
676 | |
677 | /* |
678 | * sysmon_power_make_dictionary: |
679 | * |
680 | * Adds the properties for an event in a dictionary. |
681 | */ |
682 | int |
683 | sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data, |
684 | int event, int type) |
685 | { |
686 | int i; |
687 | |
688 | KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); |
689 | |
690 | switch (type) { |
691 | /* |
692 | * create the dictionary for a power switch event. |
693 | */ |
694 | case POWER_EVENT_SWITCH_STATE_CHANGE: |
695 | { |
696 | const struct power_event_description *peevent = |
697 | pswitch_event_desc; |
698 | const struct power_event_description *petype = |
699 | pswitch_type_desc; |
700 | struct sysmon_pswitch *smpsw = |
701 | (struct sysmon_pswitch *)power_data; |
702 | const char *pwrtype = "pswitch" ; |
703 | |
704 | #define SETPROP(key, str) \ |
705 | do { \ |
706 | if ((str) != NULL && !prop_dictionary_set_cstring(dict, \ |
707 | (key), \ |
708 | (str))) { \ |
709 | printf("%s: failed to set %s\n", __func__, (str)); \ |
710 | return EINVAL; \ |
711 | } \ |
712 | } while (/* CONSTCOND */ 0) |
713 | |
714 | |
715 | SETPROP("driver-name" , smpsw->smpsw_name); |
716 | |
717 | for (i = 0; peevent[i].type != -1; i++) |
718 | if (peevent[i].type == event) |
719 | break; |
720 | |
721 | SETPROP("powerd-event-name" , peevent[i].desc); |
722 | |
723 | for (i = 0; petype[i].type != -1; i++) |
724 | if (petype[i].type == smpsw->smpsw_type) |
725 | break; |
726 | |
727 | SETPROP("powerd-script-name" , petype[i].desc); |
728 | SETPROP("power-type" , pwrtype); |
729 | break; |
730 | } |
731 | /* |
732 | * create a dictionary for power envsys event. |
733 | */ |
734 | case POWER_EVENT_ENVSYS_STATE_CHANGE: |
735 | { |
736 | const struct power_event_description *peevent = |
737 | penvsys_event_desc; |
738 | const struct power_event_description *petype = |
739 | penvsys_type_desc; |
740 | struct penvsys_state *pes = |
741 | (struct penvsys_state *)power_data; |
742 | const char *pwrtype = "envsys" ; |
743 | |
744 | SETPROP("driver-name" , pes->pes_dvname); |
745 | SETPROP("sensor-name" , pes->pes_sensname); |
746 | SETPROP("state-description" , pes->pes_statedesc); |
747 | |
748 | for (i = 0; peevent[i].type != -1; i++) |
749 | if (peevent[i].type == event) |
750 | break; |
751 | |
752 | SETPROP("powerd-event-name" , peevent[i].desc); |
753 | |
754 | for (i = 0; petype[i].type != -1; i++) |
755 | if (petype[i].type == pes->pes_type) |
756 | break; |
757 | |
758 | SETPROP("powerd-script-name" , petype[i].desc); |
759 | SETPROP("power-type" , pwrtype); |
760 | break; |
761 | } |
762 | default: |
763 | return ENOTSUP; |
764 | } |
765 | |
766 | return 0; |
767 | } |
768 | |
769 | /* |
770 | * sysmon_power_destroy_dictionary: |
771 | * |
772 | * Destroys a power_event_dictionary object and all its |
773 | * properties in the dictionary. |
774 | */ |
775 | static void |
776 | sysmon_power_destroy_dictionary(struct power_event_dictionary *ped) |
777 | { |
778 | prop_object_iterator_t iter; |
779 | prop_object_t obj; |
780 | |
781 | KASSERT(ped != NULL); |
782 | KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0); |
783 | |
784 | iter = prop_dictionary_iterator(ped->dict); |
785 | if (iter == NULL) |
786 | return; |
787 | |
788 | while ((obj = prop_object_iterator_next(iter)) != NULL) { |
789 | prop_dictionary_remove(ped->dict, |
790 | prop_dictionary_keysym_cstring_nocopy(obj)); |
791 | prop_object_iterator_reset(iter); |
792 | } |
793 | |
794 | prop_object_iterator_release(iter); |
795 | prop_object_release(ped->dict); |
796 | |
797 | kmem_free(ped, sizeof(*ped)); |
798 | } |
799 | |
800 | /* |
801 | * sysmon_power_settype: |
802 | * |
803 | * Sets the back-end power management type. This information can |
804 | * be used by the power management daemon. |
805 | */ |
806 | void |
807 | sysmon_power_settype(const char *type) |
808 | { |
809 | |
810 | /* |
811 | * Don't bother locking this; it's going to be set |
812 | * during autoconfiguration, and then only read from |
813 | * then on. |
814 | */ |
815 | (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type)); |
816 | } |
817 | |
818 | #define PENVSYS_SHOWSTATE(str) \ |
819 | do { \ |
820 | printf("%s: %s limit on '%s'\n", \ |
821 | pes->pes_dvname, (str), pes->pes_sensname); \ |
822 | } while (/* CONSTCOND */ 0) |
823 | |
824 | /* |
825 | * sysmon_penvsys_event: |
826 | * |
827 | * Puts an event onto the sysmon power queue and sends the |
828 | * appropriate event if the daemon is running, otherwise a |
829 | * message is shown. |
830 | */ |
831 | void |
832 | sysmon_penvsys_event(struct penvsys_state *pes, int event) |
833 | { |
834 | struct power_event_dictionary *ped; |
835 | const char *mystr = NULL; |
836 | |
837 | KASSERT(pes != NULL); |
838 | |
839 | rnd_add_uint32(&sysmon_rndsource, pes->pes_type); |
840 | |
841 | if (sysmon_power_daemon != NULL) { |
842 | /* |
843 | * Create a dictionary for the new event. |
844 | */ |
845 | ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); |
846 | if (!ped) |
847 | return; |
848 | ped->dict = prop_dictionary_create(); |
849 | |
850 | if (sysmon_power_daemon_task(ped, pes, event) == 0) |
851 | return; |
852 | /* We failed */ |
853 | prop_object_release(ped->dict); |
854 | kmem_free(ped, sizeof(*ped)); |
855 | } |
856 | |
857 | switch (pes->pes_type) { |
858 | case PENVSYS_TYPE_BATTERY: |
859 | switch (event) { |
860 | case PENVSYS_EVENT_LOW_POWER: |
861 | printf("sysmon: LOW POWER! SHUTTING DOWN.\n" ); |
862 | cpu_reboot(RB_POWERDOWN, NULL); |
863 | break; |
864 | case PENVSYS_EVENT_STATE_CHANGED: |
865 | printf("%s: state changed on '%s' to '%s'\n" , |
866 | pes->pes_dvname, pes->pes_sensname, |
867 | pes->pes_statedesc); |
868 | break; |
869 | case PENVSYS_EVENT_BATT_CRIT: |
870 | mystr = "critical capacity" ; |
871 | PENVSYS_SHOWSTATE(mystr); |
872 | break; |
873 | case PENVSYS_EVENT_BATT_WARN: |
874 | mystr = "warning capacity" ; |
875 | PENVSYS_SHOWSTATE(mystr); |
876 | break; |
877 | case PENVSYS_EVENT_BATT_HIGH: |
878 | mystr = "high capacity" ; |
879 | PENVSYS_SHOWSTATE(mystr); |
880 | break; |
881 | case PENVSYS_EVENT_BATT_MAX: |
882 | mystr = "maximum capacity" ; |
883 | PENVSYS_SHOWSTATE(mystr); |
884 | break; |
885 | case PENVSYS_EVENT_NORMAL: |
886 | printf("%s: normal capacity on '%s'\n" , |
887 | pes->pes_dvname, pes->pes_sensname); |
888 | break; |
889 | } |
890 | break; |
891 | case PENVSYS_TYPE_FAN: |
892 | case PENVSYS_TYPE_INDICATOR: |
893 | case PENVSYS_TYPE_TEMP: |
894 | case PENVSYS_TYPE_POWER: |
895 | case PENVSYS_TYPE_RESISTANCE: |
896 | case PENVSYS_TYPE_VOLTAGE: |
897 | switch (event) { |
898 | case PENVSYS_EVENT_CRITICAL: |
899 | mystr = "critical" ; |
900 | PENVSYS_SHOWSTATE(mystr); |
901 | break; |
902 | case PENVSYS_EVENT_CRITOVER: |
903 | mystr = "critical over" ; |
904 | PENVSYS_SHOWSTATE(mystr); |
905 | break; |
906 | case PENVSYS_EVENT_CRITUNDER: |
907 | mystr = "critical under" ; |
908 | PENVSYS_SHOWSTATE(mystr); |
909 | break; |
910 | case PENVSYS_EVENT_WARNOVER: |
911 | mystr = "warning over" ; |
912 | PENVSYS_SHOWSTATE(mystr); |
913 | break; |
914 | case PENVSYS_EVENT_WARNUNDER: |
915 | mystr = "warning under" ; |
916 | PENVSYS_SHOWSTATE(mystr); |
917 | break; |
918 | case PENVSYS_EVENT_NORMAL: |
919 | printf("%s: normal state on '%s'\n" , |
920 | pes->pes_dvname, pes->pes_sensname); |
921 | break; |
922 | default: |
923 | printf("%s: unknown event\n" , __func__); |
924 | } |
925 | break; |
926 | case PENVSYS_TYPE_DRIVE: |
927 | switch (event) { |
928 | case PENVSYS_EVENT_STATE_CHANGED: |
929 | printf("%s: state changed on '%s' to '%s'\n" , |
930 | pes->pes_dvname, pes->pes_sensname, |
931 | pes->pes_statedesc); |
932 | break; |
933 | case PENVSYS_EVENT_NORMAL: |
934 | printf("%s: normal state on '%s' (%s)\n" , |
935 | pes->pes_dvname, pes->pes_sensname, |
936 | pes->pes_statedesc); |
937 | break; |
938 | } |
939 | break; |
940 | default: |
941 | printf("%s: unknown power type\n" , __func__); |
942 | break; |
943 | } |
944 | } |
945 | |
946 | /* |
947 | * sysmon_pswitch_register: |
948 | * |
949 | * Register a power switch device. |
950 | */ |
951 | int |
952 | sysmon_pswitch_register(struct sysmon_pswitch *smpsw) |
953 | { |
954 | (void)RUN_ONCE(&once_power, power_preinit); |
955 | |
956 | return 0; |
957 | } |
958 | |
959 | /* |
960 | * sysmon_pswitch_unregister: |
961 | * |
962 | * Unregister a power switch device. |
963 | */ |
964 | void |
965 | sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) |
966 | { |
967 | /* nada */ |
968 | } |
969 | |
970 | /* |
971 | * sysmon_pswitch_event: |
972 | * |
973 | * Register an event on a power switch device. |
974 | */ |
975 | void |
976 | sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) |
977 | { |
978 | struct power_event_dictionary *ped = NULL; |
979 | |
980 | KASSERT(smpsw != NULL); |
981 | |
982 | /* |
983 | * For pnp specific events, we don't care if the power daemon |
984 | * is running or not |
985 | */ |
986 | if (smpsw->smpsw_type == PSWITCH_TYPE_LID) { |
987 | switch (event) { |
988 | case PSWITCH_EVENT_PRESSED: |
989 | pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE); |
990 | break; |
991 | case PSWITCH_EVENT_RELEASED: |
992 | pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); |
993 | break; |
994 | default: |
995 | break; |
996 | } |
997 | } |
998 | |
999 | if (sysmon_power_daemon != NULL) { |
1000 | /* |
1001 | * Create a new dictionary for the event. |
1002 | */ |
1003 | ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); |
1004 | if (!ped) |
1005 | return; |
1006 | ped->dict = prop_dictionary_create(); |
1007 | |
1008 | if (sysmon_power_daemon_task(ped, smpsw, event) == 0) |
1009 | return; |
1010 | /* We failed */ |
1011 | prop_object_release(ped->dict); |
1012 | kmem_free(ped, sizeof(*ped)); |
1013 | } |
1014 | |
1015 | switch (smpsw->smpsw_type) { |
1016 | case PSWITCH_TYPE_POWER: |
1017 | if (event != PSWITCH_EVENT_PRESSED) { |
1018 | /* just ignore it */ |
1019 | return; |
1020 | } |
1021 | |
1022 | /* |
1023 | * Attempt a somewhat graceful shutdown of the system, |
1024 | * as if the user has issued a reboot(2) call with |
1025 | * RB_POWERDOWN. |
1026 | */ |
1027 | printf("%s: power button pressed, shutting down!\n" , |
1028 | smpsw->smpsw_name); |
1029 | cpu_reboot(RB_POWERDOWN, NULL); |
1030 | break; |
1031 | |
1032 | case PSWITCH_TYPE_RESET: |
1033 | if (event != PSWITCH_EVENT_PRESSED) { |
1034 | /* just ignore it */ |
1035 | return; |
1036 | } |
1037 | |
1038 | /* |
1039 | * Attempt a somewhat graceful reboot of the system, |
1040 | * as if the user had issued a reboot(2) call. |
1041 | */ |
1042 | printf("%s: reset button pressed, rebooting!\n" , |
1043 | smpsw->smpsw_name); |
1044 | cpu_reboot(0, NULL); |
1045 | break; |
1046 | |
1047 | case PSWITCH_TYPE_SLEEP: |
1048 | if (event != PSWITCH_EVENT_PRESSED) { |
1049 | /* just ignore it */ |
1050 | return; |
1051 | } |
1052 | |
1053 | /* |
1054 | * Try to enter a "sleep" state. |
1055 | */ |
1056 | /* XXX */ |
1057 | printf("%s: sleep button pressed.\n" , smpsw->smpsw_name); |
1058 | break; |
1059 | |
1060 | case PSWITCH_TYPE_HOTKEY: |
1061 | /* |
1062 | * Eat up the event, there's nothing we can do |
1063 | */ |
1064 | break; |
1065 | |
1066 | case PSWITCH_TYPE_LID: |
1067 | switch (event) { |
1068 | case PSWITCH_EVENT_PRESSED: |
1069 | /* |
1070 | * Try to enter a "standby" state. |
1071 | */ |
1072 | /* XXX */ |
1073 | printf("%s: lid closed.\n" , smpsw->smpsw_name); |
1074 | break; |
1075 | |
1076 | case PSWITCH_EVENT_RELEASED: |
1077 | /* |
1078 | * Come out of "standby" state. |
1079 | */ |
1080 | /* XXX */ |
1081 | printf("%s: lid opened.\n" , smpsw->smpsw_name); |
1082 | break; |
1083 | |
1084 | default: |
1085 | printf("%s: unknown lid switch event: %d\n" , |
1086 | smpsw->smpsw_name, event); |
1087 | } |
1088 | break; |
1089 | |
1090 | case PSWITCH_TYPE_ACADAPTER: |
1091 | switch (event) { |
1092 | case PSWITCH_EVENT_PRESSED: |
1093 | /* |
1094 | * Come out of power-save state. |
1095 | */ |
1096 | aprint_normal("%s: AC adapter online.\n" , |
1097 | smpsw->smpsw_name); |
1098 | break; |
1099 | |
1100 | case PSWITCH_EVENT_RELEASED: |
1101 | /* |
1102 | * Try to enter a power-save state. |
1103 | */ |
1104 | aprint_normal("%s: AC adapter offline.\n" , |
1105 | smpsw->smpsw_name); |
1106 | break; |
1107 | } |
1108 | break; |
1109 | |
1110 | } |
1111 | } |
1112 | |
1113 | static |
1114 | int |
1115 | sysmon_power_modcmd(modcmd_t cmd, void *arg) |
1116 | { |
1117 | int ret; |
1118 | |
1119 | switch (cmd) { |
1120 | case MODULE_CMD_INIT: |
1121 | ret = sysmon_power_init(); |
1122 | break; |
1123 | |
1124 | case MODULE_CMD_FINI: |
1125 | ret = sysmon_power_fini(); |
1126 | break; |
1127 | |
1128 | case MODULE_CMD_STAT: |
1129 | default: |
1130 | ret = ENOTTY; |
1131 | } |
1132 | |
1133 | return ret; |
1134 | } |
1135 | |
1136 | |