File Index
Files and directories
Sign in
FILE

apm-emulation.c

File preview with syntax highlighting.

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