Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / drivers / macintosh / apm_emu.c
1 /* APM emulation layer for PowerMac
2  * 
3  * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
4  *
5  * Lots of code inherited from apm.c, see appropriate notice in
6  *  arch/i386/kernel/apm.c
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2, or (at your option) any
11  * later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  *
19  */
20
21 #include <linux/module.h>
22
23 #include <linux/poll.h>
24 #include <linux/types.h>
25 #include <linux/stddef.h>
26 #include <linux/timer.h>
27 #include <linux/fcntl.h>
28 #include <linux/slab.h>
29 #include <linux/stat.h>
30 #include <linux/proc_fs.h>
31 #include <linux/miscdevice.h>
32 #include <linux/apm_bios.h>
33 #include <linux/init.h>
34 #include <linux/sched.h>
35 #include <linux/pm.h>
36 #include <linux/kernel.h>
37 #include <linux/smp_lock.h>
38
39 #include <linux/adb.h>
40 #include <linux/pmu.h>
41
42 #include <asm/system.h>
43 #include <asm/uaccess.h>
44 #include <asm/machdep.h>
45
46 #undef DEBUG
47
48 #ifdef DEBUG
49 #define DBG(args...) printk(KERN_DEBUG args)
50 //#define DBG(args...) xmon_printf(args)
51 #else
52 #define DBG(args...) do { } while (0)
53 #endif
54
55 /*
56  * The apm_bios device is one of the misc char devices.
57  * This is its minor number.
58  */
59 #define APM_MINOR_DEV   134
60
61 /*
62  * Maximum number of events stored
63  */
64 #define APM_MAX_EVENTS          20
65
66 #define FAKE_APM_BIOS_VERSION   0x0101
67
68 #define APM_USER_NOTIFY_TIMEOUT (5*HZ)
69
70 /*
71  * The per-file APM data
72  */
73 struct apm_user {
74         int             magic;
75         struct apm_user *       next;
76         int             suser: 1;
77         int             suspend_waiting: 1;
78         int             suspends_pending;
79         int             suspends_read;
80         int             event_head;
81         int             event_tail;
82         apm_event_t     events[APM_MAX_EVENTS];
83 };
84
85 /*
86  * The magic number in apm_user
87  */
88 #define APM_BIOS_MAGIC          0x4101
89
90 /*
91  * Local variables
92  */
93 static int                      suspends_pending;
94
95 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
96 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
97 static struct apm_user *        user_list;
98
99 static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
100 static struct pmu_sleep_notifier apm_sleep_notifier = {
101         apm_notify_sleep,
102         SLEEP_LEVEL_USERLAND,
103 };
104
105 static const char driver_version[] = "0.5";     /* no spaces */
106
107 #ifdef DEBUG
108 static char *   apm_event_name[] = {
109         "system standby",
110         "system suspend",
111         "normal resume",
112         "critical resume",
113         "low battery",
114         "power status change",
115         "update time",
116         "critical suspend",
117         "user standby",
118         "user suspend",
119         "system standby resume",
120         "capabilities change"
121 };
122 #define NR_APM_EVENT_NAME       \
123                 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
124
125 #endif
126
127 static int queue_empty(struct apm_user *as)
128 {
129         return as->event_head == as->event_tail;
130 }
131
132 static apm_event_t get_queued_event(struct apm_user *as)
133 {
134         as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
135         return as->events[as->event_tail];
136 }
137
138 static void queue_event(apm_event_t event, struct apm_user *sender)
139 {
140         struct apm_user *       as;
141
142         DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
143         if (user_list == NULL)
144                 return;
145         for (as = user_list; as != NULL; as = as->next) {
146                 if (as == sender)
147                         continue;
148                 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
149                 if (as->event_head == as->event_tail) {
150                         static int notified;
151
152                         if (notified++ == 0)
153                             printk(KERN_ERR "apm_emu: an event queue overflowed\n");
154                         as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
155                 }
156                 as->events[as->event_head] = event;
157                 if (!as->suser)
158                         continue;
159                 switch (event) {
160                 case APM_SYS_SUSPEND:
161                 case APM_USER_SUSPEND:
162                         as->suspends_pending++;
163                         suspends_pending++;
164                         break;
165                 case APM_NORMAL_RESUME:
166                         as->suspend_waiting = 0;
167                         break;
168                 }
169         }
170         wake_up_interruptible(&apm_waitqueue);
171 }
172
173 static int check_apm_user(struct apm_user *as, const char *func)
174 {
175         if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
176                 printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
177                 return 1;
178         }
179         return 0;
180 }
181
182 static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
183 {
184         struct apm_user *       as;
185         size_t                  i;
186         apm_event_t             event;
187         DECLARE_WAITQUEUE(wait, current);
188
189         as = fp->private_data;
190         if (check_apm_user(as, "read"))
191                 return -EIO;
192         if (count < sizeof(apm_event_t))
193                 return -EINVAL;
194         if (queue_empty(as)) {
195                 if (fp->f_flags & O_NONBLOCK)
196                         return -EAGAIN;
197                 add_wait_queue(&apm_waitqueue, &wait);
198 repeat:
199                 set_current_state(TASK_INTERRUPTIBLE);
200                 if (queue_empty(as) && !signal_pending(current)) {
201                         schedule();
202                         goto repeat;
203                 }
204                 set_current_state(TASK_RUNNING);
205                 remove_wait_queue(&apm_waitqueue, &wait);
206         }
207         i = count;
208         while ((i >= sizeof(event)) && !queue_empty(as)) {
209                 event = get_queued_event(as);
210                 DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
211                 if (copy_to_user(buf, &event, sizeof(event))) {
212                         if (i < count)
213                                 break;
214                         return -EFAULT;
215                 }
216                 switch (event) {
217                 case APM_SYS_SUSPEND:
218                 case APM_USER_SUSPEND:
219                         as->suspends_read++;
220                         break;
221                 }
222                 buf += sizeof(event);
223                 i -= sizeof(event);
224         }
225         if (i < count)
226                 return count - i;
227         if (signal_pending(current))
228                 return -ERESTARTSYS;
229         return 0;
230 }
231
232 static unsigned int do_poll(struct file *fp, poll_table * wait)
233 {
234         struct apm_user * as;
235
236         as = fp->private_data;
237         if (check_apm_user(as, "poll"))
238                 return 0;
239         poll_wait(fp, &apm_waitqueue, wait);
240         if (!queue_empty(as))
241                 return POLLIN | POLLRDNORM;
242         return 0;
243 }
244
245 static int do_ioctl(struct inode * inode, struct file *filp,
246                     u_int cmd, u_long arg)
247 {
248         struct apm_user *       as;
249         DECLARE_WAITQUEUE(wait, current);
250
251         as = filp->private_data;
252         if (check_apm_user(as, "ioctl"))
253                 return -EIO;
254         if (!as->suser)
255                 return -EPERM;
256         switch (cmd) {
257         case APM_IOC_SUSPEND:
258                 /* If a suspend message was sent to userland, we
259                  * consider this as a confirmation message
260                  */
261                 if (as->suspends_read > 0) {
262                         as->suspends_read--;
263                         as->suspends_pending--;
264                         suspends_pending--;
265                 } else {
266                         // Route to PMU suspend ?
267                         break;
268                 }
269                 as->suspend_waiting = 1;
270                 add_wait_queue(&apm_waitqueue, &wait);
271                 DBG("apm_emu: ioctl waking up sleep waiter !\n");
272                 wake_up(&apm_suspend_waitqueue);
273                 mb();
274                 while(as->suspend_waiting && !signal_pending(current)) {
275                         set_current_state(TASK_INTERRUPTIBLE);
276                         schedule();
277                 }
278                 set_current_state(TASK_RUNNING);
279                 remove_wait_queue(&apm_waitqueue, &wait);
280                 break;
281         default:
282                 return -EINVAL;
283         }
284         return 0;
285 }
286
287 static int do_release(struct inode * inode, struct file * filp)
288 {
289         struct apm_user *       as;
290
291         as = filp->private_data;
292         if (check_apm_user(as, "release"))
293                 return 0;
294         filp->private_data = NULL;
295         lock_kernel();
296         if (as->suspends_pending > 0) {
297                 suspends_pending -= as->suspends_pending;
298                 if (suspends_pending <= 0)
299                         wake_up(&apm_suspend_waitqueue);
300         }
301         if (user_list == as)
302                 user_list = as->next;
303         else {
304                 struct apm_user *       as1;
305
306                 for (as1 = user_list;
307                      (as1 != NULL) && (as1->next != as);
308                      as1 = as1->next)
309                         ;
310                 if (as1 == NULL)
311                         printk(KERN_ERR "apm: filp not in user list\n");
312                 else
313                         as1->next = as->next;
314         }
315         unlock_kernel();
316         kfree(as);
317         return 0;
318 }
319
320 static int do_open(struct inode * inode, struct file * filp)
321 {
322         struct apm_user *       as;
323
324         as = kmalloc(sizeof(*as), GFP_KERNEL);
325         if (as == NULL) {
326                 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
327                        sizeof(*as));
328                 return -ENOMEM;
329         }
330         as->magic = APM_BIOS_MAGIC;
331         as->event_tail = as->event_head = 0;
332         as->suspends_pending = 0;
333         as->suspends_read = 0;
334         /*
335          * XXX - this is a tiny bit broken, when we consider BSD
336          * process accounting. If the device is opened by root, we
337          * instantly flag that we used superuser privs. Who knows,
338          * we might close the device immediately without doing a
339          * privileged operation -- cevans
340          */
341         as->suser = capable(CAP_SYS_ADMIN);
342         as->next = user_list;
343         user_list = as;
344         filp->private_data = as;
345
346         DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
347
348         return 0;
349 }
350
351 /* Wait for all clients to ack the suspend request. APM API
352  * doesn't provide a way to NAK, but this could be added
353  * here.
354  */
355 static void wait_all_suspend(void)
356 {
357         DECLARE_WAITQUEUE(wait, current);
358
359         add_wait_queue(&apm_suspend_waitqueue, &wait);
360         DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
361         while(suspends_pending > 0) {
362                 set_current_state(TASK_UNINTERRUPTIBLE);
363                 schedule();
364         }
365         set_current_state(TASK_RUNNING);
366         remove_wait_queue(&apm_suspend_waitqueue, &wait);
367
368         DBG("apm_emu: wait_all_suspend() - complete !\n");
369 }
370
371 static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
372 {
373         switch(when) {
374                 case PBOOK_SLEEP_REQUEST:
375                         queue_event(APM_SYS_SUSPEND, NULL);
376                         wait_all_suspend();
377                         break;
378                 case PBOOK_WAKE:
379                         queue_event(APM_NORMAL_RESUME, NULL);
380                         break;
381         }
382 }
383
384 #define APM_CRITICAL            10
385 #define APM_LOW                 30
386
387 static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
388 {
389         /* Arguments, with symbols from linux/apm_bios.h.  Information is
390            from the Get Power Status (0x0a) call unless otherwise noted.
391
392            0) Linux driver version (this will change if format changes)
393            1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
394            2) APM flags from APM Installation Check (0x00):
395               bit 0: APM_16_BIT_SUPPORT
396               bit 1: APM_32_BIT_SUPPORT
397               bit 2: APM_IDLE_SLOWS_CLOCK
398               bit 3: APM_BIOS_DISABLED
399               bit 4: APM_BIOS_DISENGAGED
400            3) AC line status
401               0x00: Off-line
402               0x01: On-line
403               0x02: On backup power (BIOS >= 1.1 only)
404               0xff: Unknown
405            4) Battery status
406               0x00: High
407               0x01: Low
408               0x02: Critical
409               0x03: Charging
410               0x04: Selected battery not present (BIOS >= 1.2 only)
411               0xff: Unknown
412            5) Battery flag
413               bit 0: High
414               bit 1: Low
415               bit 2: Critical
416               bit 3: Charging
417               bit 7: No system battery
418               0xff: Unknown
419            6) Remaining battery life (percentage of charge):
420               0-100: valid
421               -1: Unknown
422            7) Remaining battery life (time units):
423               Number of remaining minutes or seconds
424               -1: Unknown
425            8) min = minutes; sec = seconds */
426
427         unsigned short  ac_line_status;
428         unsigned short  battery_status = 0;
429         unsigned short  battery_flag   = 0xff;
430         int             percentage     = -1;
431         int             time_units     = -1;
432         int             real_count     = 0;
433         int             i;
434         char *          p = buf;
435         char            charging       = 0;
436         long            charge         = -1;
437         long            amperage       = 0;
438         unsigned long   btype          = 0;
439
440         ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
441         for (i=0; i<pmu_battery_count; i++) {
442                 if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
443                         battery_status++;
444                         if (percentage < 0)
445                                 percentage = 0;
446                         if (charge < 0)
447                                 charge = 0;
448                         percentage += (pmu_batteries[i].charge * 100) /
449                                 pmu_batteries[i].max_charge;
450                         charge += pmu_batteries[i].charge;
451                         amperage += pmu_batteries[i].amperage;
452                         if (btype == 0)
453                                 btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK);
454                         real_count++;
455                         if ((pmu_batteries[i].flags & PMU_BATT_CHARGING))
456                                 charging++;
457                 }
458         }
459         if (0 == battery_status)
460                 ac_line_status = 1;
461         battery_status = 0xff;
462         if (real_count) {
463                 if (amperage < 0) {
464                         if (btype == PMU_BATT_TYPE_SMART)
465                                 time_units = (charge * 59) / (amperage * -1);
466                         else
467                                 time_units = (charge * 16440) / (amperage * -60);
468                 }
469                 percentage /= real_count;
470                 if (charging > 0) {
471                         battery_status = 0x03;
472                         battery_flag = 0x08;
473                 } else if (percentage <= APM_CRITICAL) {
474                         battery_status = 0x02;
475                         battery_flag = 0x04;
476                 } else if (percentage <= APM_LOW) {
477                         battery_status = 0x01;
478                         battery_flag = 0x02;
479                 } else {
480                         battery_status = 0x00;
481                         battery_flag = 0x01;
482                 }
483         }
484         p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
485                      driver_version,
486                      (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
487                      FAKE_APM_BIOS_VERSION & 0xff,
488                      0,
489                      ac_line_status,
490                      battery_status,
491                      battery_flag,
492                      percentage,
493                      time_units,
494                      "min");
495
496         return p - buf;
497 }
498
499 static const struct file_operations apm_bios_fops = {
500         .owner          = THIS_MODULE,
501         .read           = do_read,
502         .poll           = do_poll,
503         .ioctl          = do_ioctl,
504         .open           = do_open,
505         .release        = do_release,
506 };
507
508 static struct miscdevice apm_device = {
509         APM_MINOR_DEV,
510         "apm_bios",
511         &apm_bios_fops
512 };
513
514 static int __init apm_emu_init(void)
515 {
516         struct proc_dir_entry *apm_proc;
517
518         if (sys_ctrler != SYS_CTRLER_PMU) {
519                 printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
520                 return -ENODEV;
521         }
522                 
523         apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
524         if (apm_proc)
525                 apm_proc->owner = THIS_MODULE;
526
527         if (misc_register(&apm_device) != 0)
528                 printk(KERN_INFO "Could not create misc. device for apm\n");
529
530         pmu_register_sleep_notifier(&apm_sleep_notifier);
531
532         printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
533
534         return 0;
535 }
536
537 static void __exit apm_emu_exit(void)
538 {
539         pmu_unregister_sleep_notifier(&apm_sleep_notifier);
540         misc_deregister(&apm_device);
541         remove_proc_entry("apm", NULL);
542
543         printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
544 }
545
546 module_init(apm_emu_init);
547 module_exit(apm_emu_exit);
548
549 MODULE_AUTHOR("Benjamin Herrenschmidt");
550 MODULE_DESCRIPTION("APM emulation layer for PowerMac");
551 MODULE_LICENSE("GPL");
552