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