^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * bios-less APM driver for ARM Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Jamey Hicks <jamey@crl.dec.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * APM 1.2 Reference:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Intel Corporation, Microsoft Corporation. Advanced Power Management
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * This document is available from Microsoft at:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * http://www.microsoft.com/whdc/archive/amp_12.mspx
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/poll.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/proc_fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/seq_file.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/apm_bios.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/capability.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/apm-emulation.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/freezer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/list.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <linux/completion.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <linux/kthread.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * One option can be changed at boot time as follows:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * apm=on/off enable/disable APM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * Maximum number of events stored
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #define APM_MAX_EVENTS 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) struct apm_queue {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) unsigned int event_head;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) unsigned int event_tail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) apm_event_t events[APM_MAX_EVENTS];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * thread states (for threads using a writable /dev/apm_bios fd):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * SUSPEND_NONE: nothing happening
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) * SUSPEND_PENDING: suspend event queued for thread and pending to be read
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * SUSPEND_READ: suspend event read, pending acknowledgement
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * SUSPEND_ACKED: acknowledgement received from thread (via ioctl),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * waiting for resume
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * SUSPEND_ACKTO: acknowledgement timeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * SUSPEND_DONE: thread had acked suspend and is now notified of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * resume
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * SUSPEND_WAIT: this thread invoked suspend and is waiting for resume
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * A thread migrates in one of three paths:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * NONE -1-> PENDING -2-> READ -3-> ACKED -4-> DONE -5-> NONE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * -6-> ACKTO -7-> NONE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * NONE -8-> WAIT -9-> NONE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * While in PENDING or READ, the thread is accounted for in the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * suspend_acks_pending counter.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * The transitions are invoked as follows:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * 1: suspend event is signalled from the core PM code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) * 2: the suspend event is read from the fd by the userspace thread
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) * 3: userspace thread issues the APM_IOC_SUSPEND ioctl (as ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) * 4: core PM code signals that we have resumed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) * 5: APM_IOC_SUSPEND ioctl returns
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) * 6: the notifier invoked from the core PM code timed out waiting
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) * for all relevant threds to enter ACKED state and puts those
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) * that haven't into ACKTO
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) * 7: those threads issue APM_IOC_SUSPEND ioctl too late,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * get an error
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * 8: userspace thread issues the APM_IOC_SUSPEND ioctl (to suspend),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) * ioctl code invokes pm_suspend()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * 9: pm_suspend() returns indicating resume
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) enum apm_suspend_state {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) SUSPEND_NONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) SUSPEND_PENDING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) SUSPEND_READ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) SUSPEND_ACKED,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) SUSPEND_ACKTO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) SUSPEND_WAIT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) SUSPEND_DONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) * The per-file APM data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct apm_user {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct list_head list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) unsigned int suser: 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) unsigned int writer: 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) unsigned int reader: 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) int suspend_result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) enum apm_suspend_state suspend_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) struct apm_queue queue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) * Local variables
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static atomic_t suspend_acks_pending = ATOMIC_INIT(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) static atomic_t userspace_notification_inhibit = ATOMIC_INIT(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static int apm_disabled;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static struct task_struct *kapmd_tsk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) * This is a list of everyone who has opened /dev/apm_bios
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) static DECLARE_RWSEM(user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) static LIST_HEAD(apm_user_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) * kapmd info. kapmd provides us a process context to handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) * "APM" events within - specifically necessary if we're going
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) * to be suspending the system.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static DEFINE_SPINLOCK(kapmd_queue_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static struct apm_queue kapmd_queue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static DEFINE_MUTEX(state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static const char driver_version[] = "1.13"; /* no spaces */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) * Compatibility cruft until the IPAQ people move over to the new
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) * interface.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static void __apm_get_power_status(struct apm_power_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) * This allows machines to provide their own "apm get power status" function.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) EXPORT_SYMBOL(apm_get_power_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * APM event queue management.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) static inline int queue_empty(struct apm_queue *q)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) return q->event_head == q->event_tail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) static inline apm_event_t queue_get_event(struct apm_queue *q)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) return q->events[q->event_tail];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) static void queue_add_event(struct apm_queue *q, apm_event_t event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) if (q->event_head == q->event_tail) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) static int notified;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if (notified++ == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) printk(KERN_ERR "apm: an event queue overflowed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) q->events[q->event_head] = event;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) static void queue_event(apm_event_t event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) struct apm_user *as;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) down_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) list_for_each_entry(as, &apm_user_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) if (as->reader)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) queue_add_event(&as->queue, event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) up_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) wake_up_interruptible(&apm_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) struct apm_user *as = fp->private_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) apm_event_t event;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) int i = count, ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) if (count < sizeof(apm_event_t))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return -EAGAIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) event = queue_get_event(&as->queue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) ret = -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) if (copy_to_user(buf, &event, sizeof(event)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) if (as->suspend_state == SUSPEND_PENDING &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) as->suspend_state = SUSPEND_READ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) buf += sizeof(event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) i -= sizeof(event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) if (i < count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) ret = count - i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) static __poll_t apm_poll(struct file *fp, poll_table * wait)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) struct apm_user *as = fp->private_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) poll_wait(fp, &apm_waitqueue, wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) return queue_empty(&as->queue) ? 0 : EPOLLIN | EPOLLRDNORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) * apm_ioctl - handle APM ioctl
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) * APM_IOC_SUSPEND
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) * This IOCTL is overloaded, and performs two functions. It is used to:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) * - initiate a suspend
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) * - acknowledge a suspend read from /dev/apm_bios.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) * Only when everyone who has opened /dev/apm_bios with write permission
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) * has acknowledge does the actual suspend happen.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) static long
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) apm_ioctl(struct file *filp, u_int cmd, u_long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) struct apm_user *as = filp->private_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) int err = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) if (!as->suser || !as->writer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) case APM_IOC_SUSPEND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) as->suspend_result = -EINTR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) switch (as->suspend_state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) case SUSPEND_READ:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) * If we read a suspend command from /dev/apm_bios,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) * then the corresponding APM_IOC_SUSPEND ioctl is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) * interpreted as an acknowledge.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) as->suspend_state = SUSPEND_ACKED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) atomic_dec(&suspend_acks_pending);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) * suspend_acks_pending changed, the notifier needs to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) * be woken up for this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) wake_up(&apm_suspend_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) * Wait for the suspend/resume to complete. If there
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * are pending acknowledges, we wait here for them.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) * wait_event_freezable() is interruptible and pending
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) * signal can cause busy looping. We aren't doing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) * anything critical, chill a bit on each iteration.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) while (wait_event_freezable(apm_suspend_waitqueue,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) as->suspend_state != SUSPEND_ACKED))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) msleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) case SUSPEND_ACKTO:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) as->suspend_result = -ETIMEDOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) as->suspend_state = SUSPEND_WAIT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) * Otherwise it is a request to suspend the system.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) * Just invoke pm_suspend(), we'll handle it from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) * there via the notifier.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) as->suspend_result = pm_suspend(PM_SUSPEND_MEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) err = as->suspend_result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) as->suspend_state = SUSPEND_NONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) static int apm_release(struct inode * inode, struct file * filp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) struct apm_user *as = filp->private_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) filp->private_data = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) down_write(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) list_del(&as->list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) up_write(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) * We are now unhooked from the chain. As far as new
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) * events are concerned, we no longer exist.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) if (as->suspend_state == SUSPEND_PENDING ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) as->suspend_state == SUSPEND_READ)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) atomic_dec(&suspend_acks_pending);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) wake_up(&apm_suspend_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) kfree(as);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static int apm_open(struct inode * inode, struct file * filp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) struct apm_user *as;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) as = kzalloc(sizeof(*as), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) if (as) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) * XXX - this is a tiny bit broken, when we consider BSD
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) * process accounting. If the device is opened by root, we
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) * instantly flag that we used superuser privs. Who knows,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) * we might close the device immediately without doing a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) * privileged operation -- cevans
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) as->suser = capable(CAP_SYS_ADMIN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) down_write(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) list_add(&as->list, &apm_user_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) up_write(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) filp->private_data = as;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) return as ? 0 : -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) static const struct file_operations apm_bios_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) .read = apm_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) .poll = apm_poll,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) .unlocked_ioctl = apm_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) .open = apm_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) .release = apm_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) .llseek = noop_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) static struct miscdevice apm_device = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) .minor = APM_MINOR_DEV,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) .name = "apm_bios",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) .fops = &apm_bios_fops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) * Arguments, with symbols from linux/apm_bios.h.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) * 0) Linux driver version (this will change if format changes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) * 2) APM flags from APM Installation Check (0x00):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) * bit 0: APM_16_BIT_SUPPORT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) * bit 1: APM_32_BIT_SUPPORT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) * bit 2: APM_IDLE_SLOWS_CLOCK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) * bit 3: APM_BIOS_DISABLED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) * bit 4: APM_BIOS_DISENGAGED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) * 3) AC line status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) * 0x00: Off-line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) * 0x01: On-line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) * 0x02: On backup power (BIOS >= 1.1 only)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) * 0xff: Unknown
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) * 4) Battery status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) * 0x00: High
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) * 0x01: Low
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) * 0x02: Critical
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) * 0x03: Charging
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) * 0x04: Selected battery not present (BIOS >= 1.2 only)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) * 0xff: Unknown
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) * 5) Battery flag
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) * bit 0: High
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) * bit 1: Low
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) * bit 2: Critical
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) * bit 3: Charging
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) * bit 7: No system battery
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) * 0xff: Unknown
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) * 6) Remaining battery life (percentage of charge):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) * 0-100: valid
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) * -1: Unknown
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) * 7) Remaining battery life (time units):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) * Number of remaining minutes or seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) * -1: Unknown
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) * 8) min = minutes; sec = seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) static int proc_apm_show(struct seq_file *m, void *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) struct apm_power_info info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) char *units;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) info.ac_line_status = 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) info.battery_status = 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) info.battery_flag = 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) info.battery_life = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) info.time = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) info.units = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) if (apm_get_power_status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) apm_get_power_status(&info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) switch (info.units) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) default: units = "?"; break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) case 0: units = "min"; break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) case 1: units = "sec"; break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) seq_printf(m, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) driver_version, APM_32_BIT_SUPPORT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) info.ac_line_status, info.battery_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) info.battery_flag, info.battery_life,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) info.time, units);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) static int kapmd(void *arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) apm_event_t event;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) wait_event_interruptible(kapmd_wait,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) !queue_empty(&kapmd_queue) || kthread_should_stop());
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) if (kthread_should_stop())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) spin_lock_irq(&kapmd_queue_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) event = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) if (!queue_empty(&kapmd_queue))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) event = queue_get_event(&kapmd_queue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) spin_unlock_irq(&kapmd_queue_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) switch (event) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) case 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) case APM_LOW_BATTERY:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) case APM_POWER_STATUS_CHANGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) queue_event(event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) case APM_USER_SUSPEND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) case APM_SYS_SUSPEND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) pm_suspend(PM_SUSPEND_MEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) case APM_CRITICAL_SUSPEND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) atomic_inc(&userspace_notification_inhibit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) pm_suspend(PM_SUSPEND_MEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) atomic_dec(&userspace_notification_inhibit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) } while (1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) static int apm_suspend_notifier(struct notifier_block *nb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) unsigned long event,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) void *dummy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) struct apm_user *as;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) unsigned long apm_event;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) /* short-cut emergency suspends */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (atomic_read(&userspace_notification_inhibit))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) switch (event) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) case PM_SUSPEND_PREPARE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) case PM_HIBERNATION_PREPARE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) apm_event = (event == PM_SUSPEND_PREPARE) ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) APM_USER_SUSPEND : APM_USER_HIBERNATION;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) * Queue an event to all "writer" users that we want
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) * to suspend and need their ack.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) down_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) list_for_each_entry(as, &apm_user_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) if (as->suspend_state != SUSPEND_WAIT && as->reader &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) as->writer && as->suser) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) as->suspend_state = SUSPEND_PENDING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) atomic_inc(&suspend_acks_pending);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) queue_add_event(&as->queue, apm_event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) up_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) wake_up_interruptible(&apm_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) * Wait for the the suspend_acks_pending variable to drop to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) * zero, meaning everybody acked the suspend event (or the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) * process was killed.)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) * If the app won't answer within a short while we assume it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) * locked up and ignore it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) err = wait_event_interruptible_timeout(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) apm_suspend_waitqueue,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) atomic_read(&suspend_acks_pending) == 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) 5*HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) /* timed out */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) if (err == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) * Move anybody who timed out to "ack timeout" state.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) * We could time out and the userspace does the ACK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) * right after we time out but before we enter the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) * locked section here, but that's fine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) down_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) list_for_each_entry(as, &apm_user_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) if (as->suspend_state == SUSPEND_PENDING ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) as->suspend_state == SUSPEND_READ) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) as->suspend_state = SUSPEND_ACKTO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) atomic_dec(&suspend_acks_pending);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) up_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) /* let suspend proceed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) return NOTIFY_OK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) /* interrupted by signal */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) return notifier_from_errno(err);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) case PM_POST_SUSPEND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) case PM_POST_HIBERNATION:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) apm_event = (event == PM_POST_SUSPEND) ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) * Anyone on the APM queues will think we're still suspended.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) * Send a message so everyone knows we're now awake again.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) queue_event(apm_event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) * Finally, wake up anyone who is sleeping on the suspend.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) mutex_lock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) down_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) list_for_each_entry(as, &apm_user_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) if (as->suspend_state == SUSPEND_ACKED) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) * TODO: maybe grab error code, needs core
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) * changes to push the error to the notifier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) * chain (could use the second parameter if
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) * implemented)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) as->suspend_result = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) as->suspend_state = SUSPEND_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) up_read(&user_list_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) mutex_unlock(&state_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) wake_up(&apm_suspend_waitqueue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) return NOTIFY_OK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) static struct notifier_block apm_notif_block = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) .notifier_call = apm_suspend_notifier,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) static int __init apm_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) if (apm_disabled) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) printk(KERN_NOTICE "apm: disabled on user request.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) kapmd_tsk = kthread_create(kapmd, NULL, "kapmd");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) if (IS_ERR(kapmd_tsk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) ret = PTR_ERR(kapmd_tsk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) kapmd_tsk = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) wake_up_process(kapmd_tsk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) proc_create_single("apm", 0, NULL, proc_apm_show);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) ret = misc_register(&apm_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) goto out_stop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) ret = register_pm_notifier(&apm_notif_block);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) goto out_unregister;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) out_unregister:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) misc_deregister(&apm_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) out_stop:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) remove_proc_entry("apm", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) kthread_stop(kapmd_tsk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) static void __exit apm_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) unregister_pm_notifier(&apm_notif_block);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) misc_deregister(&apm_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) remove_proc_entry("apm", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) kthread_stop(kapmd_tsk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) module_init(apm_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) module_exit(apm_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) MODULE_AUTHOR("Stephen Rothwell");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) MODULE_DESCRIPTION("Advanced Power Management");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) #ifndef MODULE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) static int __init apm_setup(char *str)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) while ((str != NULL) && (*str != '\0')) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) if (strncmp(str, "off", 3) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) apm_disabled = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) if (strncmp(str, "on", 2) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) apm_disabled = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) str = strchr(str, ',');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) if (str != NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) str += strspn(str, ", \t");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) __setup("apm=", apm_setup);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) * apm_queue_event - queue an APM event for kapmd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) * @event: APM event
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) * Queue an APM event for kapmd to process and ultimately take the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) * appropriate action. Only a subset of events are handled:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) * %APM_LOW_BATTERY
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) * %APM_POWER_STATUS_CHANGE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) * %APM_USER_SUSPEND
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) * %APM_SYS_SUSPEND
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) * %APM_CRITICAL_SUSPEND
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) void apm_queue_event(apm_event_t event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) spin_lock_irqsave(&kapmd_queue_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) queue_add_event(&kapmd_queue, event);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) spin_unlock_irqrestore(&kapmd_queue_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) wake_up_interruptible(&kapmd_wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) EXPORT_SYMBOL(apm_queue_event);