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