1
2
3
4
5
6
7
8
9
10
11
12
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
37
38
39
40
41
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
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
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
129
130static DECLARE_RWSEM(user_list_lock);
131static LIST_HEAD(apm_user_list);
132
133
134
135
136
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
144static const char driver_version[] = "1.13";
145
146
147
148
149
150
151
152static void __apm_get_power_status(struct apm_power_info *info)
153{
154}
155
156
157
158
159void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status;
160EXPORT_SYMBOL(apm_get_power_status);
161
162
163
164
165
166static inline int queue_empty(struct apm_queue *q)
167{
168 return q->event_head == q->event_tail;
169}
170
171static inline apm_event_t queue_get_event(struct apm_queue *q)
172{
173 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
174 return q->events[q->event_tail];
175}
176
177static void queue_add_event(struct apm_queue *q, apm_event_t event)
178{
179 q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
180 if (q->event_head == q->event_tail) {
181 static int notified;
182
183 if (notified++ == 0)
184 printk(KERN_ERR "apm: an event queue overflowed\n");
185 q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
186 }
187 q->events[q->event_head] = event;
188}
189
190static void queue_event(apm_event_t event)
191{
192 struct apm_user *as;
193
194 down_read(&user_list_lock);
195 list_for_each_entry(as, &apm_user_list, list) {
196 if (as->reader)
197 queue_add_event(&as->queue, event);
198 }
199 up_read(&user_list_lock);
200 wake_up_interruptible(&apm_waitqueue);
201}
202
203static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
204{
205 struct apm_user *as = fp->private_data;
206 apm_event_t event;
207 int i = count, ret = 0;
208
209 if (count < sizeof(apm_event_t))
210 return -EINVAL;
211
212 if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
213 return -EAGAIN;
214
215 wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
216
217 while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
218 event = queue_get_event(&as->queue);
219
220 ret = -EFAULT;
221 if (copy_to_user(buf, &event, sizeof(event)))
222 break;
223
224 mutex_lock(&state_lock);
225 if (as->suspend_state == SUSPEND_PENDING &&
226 (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND))
227 as->suspend_state = SUSPEND_READ;
228 mutex_unlock(&state_lock);
229
230 buf += sizeof(event);
231 i -= sizeof(event);
232 }
233
234 if (i < count)
235 ret = count - i;
236
237 return ret;
238}
239
240static __poll_t apm_poll(struct file *fp, poll_table * wait)
241{
242 struct apm_user *as = fp->private_data;
243
244 poll_wait(fp, &apm_waitqueue, wait);
245 return queue_empty(&as->queue) ? 0 : EPOLLIN | EPOLLRDNORM;
246}
247
248
249
250
251
252
253
254
255
256
257
258static long
259apm_ioctl(struct file *filp, u_int cmd, u_long arg)
260{
261 struct apm_user *as = filp->private_data;
262 int err = -EINVAL;
263
264 if (!as->suser || !as->writer)
265 return -EPERM;
266
267 switch (cmd) {
268 case APM_IOC_SUSPEND:
269 mutex_lock(&state_lock);
270
271 as->suspend_result = -EINTR;
272
273 switch (as->suspend_state) {
274 case SUSPEND_READ:
275
276
277
278
279
280 as->suspend_state = SUSPEND_ACKED;
281 atomic_dec(&suspend_acks_pending);
282 mutex_unlock(&state_lock);
283
284
285
286
287
288 wake_up(&apm_suspend_waitqueue);
289
290
291
292
293
294
295
296
297 while (wait_event_freezable(apm_suspend_waitqueue,
298 as->suspend_state != SUSPEND_ACKED))
299 msleep(10);
300 break;
301 case SUSPEND_ACKTO:
302 as->suspend_result = -ETIMEDOUT;
303 mutex_unlock(&state_lock);
304 break;
305 default:
306 as->suspend_state = SUSPEND_WAIT;
307 mutex_unlock(&state_lock);
308
309
310
311
312
313
314 as->suspend_result = pm_suspend(PM_SUSPEND_MEM);
315 }
316
317 mutex_lock(&state_lock);
318 err = as->suspend_result;
319 as->suspend_state = SUSPEND_NONE;
320 mutex_unlock(&state_lock);
321 break;
322 }
323
324 return err;
325}
326
327static int apm_release(struct inode * inode, struct file * filp)
328{
329 struct apm_user *as = filp->private_data;
330
331 filp->private_data = NULL;
332
333 down_write(&user_list_lock);
334 list_del(&as->list);
335 up_write(&user_list_lock);
336
337
338
339
340
341 mutex_lock(&state_lock);
342 if (as->suspend_state == SUSPEND_PENDING ||
343 as->suspend_state == SUSPEND_READ)
344 atomic_dec(&suspend_acks_pending);
345 mutex_unlock(&state_lock);
346
347 wake_up(&apm_suspend_waitqueue);
348
349 kfree(as);
350 return 0;
351}
352
353static int apm_open(struct inode * inode, struct file * filp)
354{
355 struct apm_user *as;
356
357 as = kzalloc(sizeof(*as), GFP_KERNEL);
358 if (as) {
359
360
361
362
363
364
365
366 as->suser = capable(CAP_SYS_ADMIN);
367 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
368 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
369
370 down_write(&user_list_lock);
371 list_add(&as->list, &apm_user_list);
372 up_write(&user_list_lock);
373
374 filp->private_data = as;
375 }
376
377 return as ? 0 : -ENOMEM;
378}
379
380static const struct file_operations apm_bios_fops = {
381 .owner = THIS_MODULE,
382 .read = apm_read,
383 .poll = apm_poll,
384 .unlocked_ioctl = apm_ioctl,
385 .open = apm_open,
386 .release = apm_release,
387 .llseek = noop_llseek,
388};
389
390static struct miscdevice apm_device = {
391 .minor = APM_MINOR_DEV,
392 .name = "apm_bios",
393 .fops = &apm_bios_fops
394};
395
396
397#ifdef CONFIG_PROC_FS
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436static int proc_apm_show(struct seq_file *m, void *v)
437{
438 struct apm_power_info info;
439 char *units;
440
441 info.ac_line_status = 0xff;
442 info.battery_status = 0xff;
443 info.battery_flag = 0xff;
444 info.battery_life = -1;
445 info.time = -1;
446 info.units = -1;
447
448 if (apm_get_power_status)
449 apm_get_power_status(&info);
450
451 switch (info.units) {
452 default: units = "?"; break;
453 case 0: units = "min"; break;
454 case 1: units = "sec"; break;
455 }
456
457 seq_printf(m, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
458 driver_version, APM_32_BIT_SUPPORT,
459 info.ac_line_status, info.battery_status,
460 info.battery_flag, info.battery_life,
461 info.time, units);
462
463 return 0;
464}
465#endif
466
467static int kapmd(void *arg)
468{
469 do {
470 apm_event_t event;
471
472 wait_event_interruptible(kapmd_wait,
473 !queue_empty(&kapmd_queue) || kthread_should_stop());
474
475 if (kthread_should_stop())
476 break;
477
478 spin_lock_irq(&kapmd_queue_lock);
479 event = 0;
480 if (!queue_empty(&kapmd_queue))
481 event = queue_get_event(&kapmd_queue);
482 spin_unlock_irq(&kapmd_queue_lock);
483
484 switch (event) {
485 case 0:
486 break;
487
488 case APM_LOW_BATTERY:
489 case APM_POWER_STATUS_CHANGE:
490 queue_event(event);
491 break;
492
493 case APM_USER_SUSPEND:
494 case APM_SYS_SUSPEND:
495 pm_suspend(PM_SUSPEND_MEM);
496 break;
497
498 case APM_CRITICAL_SUSPEND:
499 atomic_inc(&userspace_notification_inhibit);
500 pm_suspend(PM_SUSPEND_MEM);
501 atomic_dec(&userspace_notification_inhibit);
502 break;
503 }
504 } while (1);
505
506 return 0;
507}
508
509static int apm_suspend_notifier(struct notifier_block *nb,
510 unsigned long event,
511 void *dummy)
512{
513 struct apm_user *as;
514 int err;
515 unsigned long apm_event;
516
517
518 if (atomic_read(&userspace_notification_inhibit))
519 return NOTIFY_DONE;
520
521 switch (event) {
522 case PM_SUSPEND_PREPARE:
523 case PM_HIBERNATION_PREPARE:
524 apm_event = (event == PM_SUSPEND_PREPARE) ?
525 APM_USER_SUSPEND : APM_USER_HIBERNATION;
526
527
528
529
530 mutex_lock(&state_lock);
531 down_read(&user_list_lock);
532
533 list_for_each_entry(as, &apm_user_list, list) {
534 if (as->suspend_state != SUSPEND_WAIT && as->reader &&
535 as->writer && as->suser) {
536 as->suspend_state = SUSPEND_PENDING;
537 atomic_inc(&suspend_acks_pending);
538 queue_add_event(&as->queue, apm_event);
539 }
540 }
541
542 up_read(&user_list_lock);
543 mutex_unlock(&state_lock);
544 wake_up_interruptible(&apm_waitqueue);
545
546
547
548
549
550
551
552
553
554 err = wait_event_interruptible_timeout(
555 apm_suspend_waitqueue,
556 atomic_read(&suspend_acks_pending) == 0,
557 5*HZ);
558
559
560 if (err == 0) {
561
562
563
564
565
566
567
568 mutex_lock(&state_lock);
569 down_read(&user_list_lock);
570 list_for_each_entry(as, &apm_user_list, list) {
571 if (as->suspend_state == SUSPEND_PENDING ||
572 as->suspend_state == SUSPEND_READ) {
573 as->suspend_state = SUSPEND_ACKTO;
574 atomic_dec(&suspend_acks_pending);
575 }
576 }
577 up_read(&user_list_lock);
578 mutex_unlock(&state_lock);
579 }
580
581
582 if (err >= 0)
583 return NOTIFY_OK;
584
585
586 return notifier_from_errno(err);
587
588 case PM_POST_SUSPEND:
589 case PM_POST_HIBERNATION:
590 apm_event = (event == PM_POST_SUSPEND) ?
591 APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
592
593
594
595
596 queue_event(apm_event);
597
598
599
600
601 mutex_lock(&state_lock);
602 down_read(&user_list_lock);
603 list_for_each_entry(as, &apm_user_list, list) {
604 if (as->suspend_state == SUSPEND_ACKED) {
605
606
607
608
609
610
611 as->suspend_result = 0;
612 as->suspend_state = SUSPEND_DONE;
613 }
614 }
615 up_read(&user_list_lock);
616 mutex_unlock(&state_lock);
617
618 wake_up(&apm_suspend_waitqueue);
619 return NOTIFY_OK;
620
621 default:
622 return NOTIFY_DONE;
623 }
624}
625
626static struct notifier_block apm_notif_block = {
627 .notifier_call = apm_suspend_notifier,
628};
629
630static int __init apm_init(void)
631{
632 int ret;
633
634 if (apm_disabled) {
635 printk(KERN_NOTICE "apm: disabled on user request.\n");
636 return -ENODEV;
637 }
638
639 kapmd_tsk = kthread_create(kapmd, NULL, "kapmd");
640 if (IS_ERR(kapmd_tsk)) {
641 ret = PTR_ERR(kapmd_tsk);
642 kapmd_tsk = NULL;
643 goto out;
644 }
645 wake_up_process(kapmd_tsk);
646
647#ifdef CONFIG_PROC_FS
648 proc_create_single("apm", 0, NULL, proc_apm_show);
649#endif
650
651 ret = misc_register(&apm_device);
652 if (ret)
653 goto out_stop;
654
655 ret = register_pm_notifier(&apm_notif_block);
656 if (ret)
657 goto out_unregister;
658
659 return 0;
660
661 out_unregister:
662 misc_deregister(&apm_device);
663 out_stop:
664 remove_proc_entry("apm", NULL);
665 kthread_stop(kapmd_tsk);
666 out:
667 return ret;
668}
669
670static void __exit apm_exit(void)
671{
672 unregister_pm_notifier(&apm_notif_block);
673 misc_deregister(&apm_device);
674 remove_proc_entry("apm", NULL);
675
676 kthread_stop(kapmd_tsk);
677}
678
679module_init(apm_init);
680module_exit(apm_exit);
681
682MODULE_AUTHOR("Stephen Rothwell");
683MODULE_DESCRIPTION("Advanced Power Management");
684MODULE_LICENSE("GPL");
685
686#ifndef MODULE
687static int __init apm_setup(char *str)
688{
689 while ((str != NULL) && (*str != '\0')) {
690 if (strncmp(str, "off", 3) == 0)
691 apm_disabled = 1;
692 if (strncmp(str, "on", 2) == 0)
693 apm_disabled = 0;
694 str = strchr(str, ',');
695 if (str != NULL)
696 str += strspn(str, ", \t");
697 }
698 return 1;
699}
700
701__setup("apm=", apm_setup);
702#endif
703
704
705
706
707
708
709
710
711
712
713
714
715
716void apm_queue_event(apm_event_t event)
717{
718 unsigned long flags;
719
720 spin_lock_irqsave(&kapmd_queue_lock, flags);
721 queue_add_event(&kapmd_queue, event);
722 spin_unlock_irqrestore(&kapmd_queue_lock, flags);
723
724 wake_up_interruptible(&kapmd_wait);
725}
726EXPORT_SYMBOL(apm_queue_event);
727