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