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