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 unsigned long apm_event;
535
536
537 if (atomic_read(&userspace_notification_inhibit))
538 return NOTIFY_DONE;
539
540 switch (event) {
541 case PM_SUSPEND_PREPARE:
542 case PM_HIBERNATION_PREPARE:
543 apm_event = (event == PM_SUSPEND_PREPARE) ?
544 APM_USER_SUSPEND : APM_USER_HIBERNATION;
545
546
547
548
549 mutex_lock(&state_lock);
550 down_read(&user_list_lock);
551
552 list_for_each_entry(as, &apm_user_list, list) {
553 if (as->suspend_state != SUSPEND_WAIT && as->reader &&
554 as->writer && as->suser) {
555 as->suspend_state = SUSPEND_PENDING;
556 atomic_inc(&suspend_acks_pending);
557 queue_add_event(&as->queue, apm_event);
558 }
559 }
560
561 up_read(&user_list_lock);
562 mutex_unlock(&state_lock);
563 wake_up_interruptible(&apm_waitqueue);
564
565
566
567
568
569
570
571
572
573 err = wait_event_interruptible_timeout(
574 apm_suspend_waitqueue,
575 atomic_read(&suspend_acks_pending) == 0,
576 5*HZ);
577
578
579 if (err == 0) {
580
581
582
583
584
585
586
587 mutex_lock(&state_lock);
588 down_read(&user_list_lock);
589 list_for_each_entry(as, &apm_user_list, list) {
590 if (as->suspend_state == SUSPEND_PENDING ||
591 as->suspend_state == SUSPEND_READ) {
592 as->suspend_state = SUSPEND_ACKTO;
593 atomic_dec(&suspend_acks_pending);
594 }
595 }
596 up_read(&user_list_lock);
597 mutex_unlock(&state_lock);
598 }
599
600
601 if (err >= 0)
602 return NOTIFY_OK;
603
604
605 return notifier_from_errno(err);
606
607 case PM_POST_SUSPEND:
608 case PM_POST_HIBERNATION:
609 apm_event = (event == PM_POST_SUSPEND) ?
610 APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
611
612
613
614
615 queue_event(apm_event);
616
617
618
619
620 mutex_lock(&state_lock);
621 down_read(&user_list_lock);
622 list_for_each_entry(as, &apm_user_list, list) {
623 if (as->suspend_state == SUSPEND_ACKED) {
624
625
626
627
628
629
630 as->suspend_result = 0;
631 as->suspend_state = SUSPEND_DONE;
632 }
633 }
634 up_read(&user_list_lock);
635 mutex_unlock(&state_lock);
636
637 wake_up(&apm_suspend_waitqueue);
638 return NOTIFY_OK;
639
640 default:
641 return NOTIFY_DONE;
642 }
643}
644
645static struct notifier_block apm_notif_block = {
646 .notifier_call = apm_suspend_notifier,
647};
648
649static int __init apm_init(void)
650{
651 int ret;
652
653 if (apm_disabled) {
654 printk(KERN_NOTICE "apm: disabled on user request.\n");
655 return -ENODEV;
656 }
657
658 kapmd_tsk = kthread_create(kapmd, NULL, "kapmd");
659 if (IS_ERR(kapmd_tsk)) {
660 ret = PTR_ERR(kapmd_tsk);
661 kapmd_tsk = NULL;
662 goto out;
663 }
664 wake_up_process(kapmd_tsk);
665
666#ifdef CONFIG_PROC_FS
667 proc_create("apm", 0, NULL, &apm_proc_fops);
668#endif
669
670 ret = misc_register(&apm_device);
671 if (ret)
672 goto out_stop;
673
674 ret = register_pm_notifier(&apm_notif_block);
675 if (ret)
676 goto out_unregister;
677
678 return 0;
679
680 out_unregister:
681 misc_deregister(&apm_device);
682 out_stop:
683 remove_proc_entry("apm", NULL);
684 kthread_stop(kapmd_tsk);
685 out:
686 return ret;
687}
688
689static void __exit apm_exit(void)
690{
691 unregister_pm_notifier(&apm_notif_block);
692 misc_deregister(&apm_device);
693 remove_proc_entry("apm", NULL);
694
695 kthread_stop(kapmd_tsk);
696}
697
698module_init(apm_init);
699module_exit(apm_exit);
700
701MODULE_AUTHOR("Stephen Rothwell");
702MODULE_DESCRIPTION("Advanced Power Management");
703MODULE_LICENSE("GPL");
704
705#ifndef MODULE
706static int __init apm_setup(char *str)
707{
708 while ((str != NULL) && (*str != '\0')) {
709 if (strncmp(str, "off", 3) == 0)
710 apm_disabled = 1;
711 if (strncmp(str, "on", 2) == 0)
712 apm_disabled = 0;
713 str = strchr(str, ',');
714 if (str != NULL)
715 str += strspn(str, ", \t");
716 }
717 return 1;
718}
719
720__setup("apm=", apm_setup);
721#endif
722
723
724
725
726
727
728
729
730
731
732
733
734
735void apm_queue_event(apm_event_t event)
736{
737 unsigned long flags;
738
739 spin_lock_irqsave(&kapmd_queue_lock, flags);
740 queue_add_event(&kapmd_queue, event);
741 spin_unlock_irqrestore(&kapmd_queue_lock, flags);
742
743 wake_up_interruptible(&kapmd_wait);
744}
745EXPORT_SYMBOL(apm_queue_event);
746