[netdrvr] ewrk3: correct card detection bug
[linux-2.6] / 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 int 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 int 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         return 1;
371 }
372
373 static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
374 {
375         switch(when) {
376                 case PBOOK_SLEEP_REQUEST:
377                         queue_event(APM_SYS_SUSPEND, NULL);
378                         if (!wait_all_suspend())
379                                 return PBOOK_SLEEP_REFUSE;
380                         break;
381                 case PBOOK_SLEEP_REJECT:
382                 case PBOOK_WAKE:
383                         queue_event(APM_NORMAL_RESUME, NULL);
384                         break;
385         }
386         return PBOOK_SLEEP_OK;
387 }
388
389 #define APM_CRITICAL            10
390 #define APM_LOW                 30
391
392 static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
393 {
394         /* Arguments, with symbols from linux/apm_bios.h.  Information is
395            from the Get Power Status (0x0a) call unless otherwise noted.
396
397            0) Linux driver version (this will change if format changes)
398            1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
399            2) APM flags from APM Installation Check (0x00):
400               bit 0: APM_16_BIT_SUPPORT
401               bit 1: APM_32_BIT_SUPPORT
402               bit 2: APM_IDLE_SLOWS_CLOCK
403               bit 3: APM_BIOS_DISABLED
404               bit 4: APM_BIOS_DISENGAGED
405            3) AC line status
406               0x00: Off-line
407               0x01: On-line
408               0x02: On backup power (BIOS >= 1.1 only)
409               0xff: Unknown
410            4) Battery status
411               0x00: High
412               0x01: Low
413               0x02: Critical
414               0x03: Charging
415               0x04: Selected battery not present (BIOS >= 1.2 only)
416               0xff: Unknown
417            5) Battery flag
418               bit 0: High
419               bit 1: Low
420               bit 2: Critical
421               bit 3: Charging
422               bit 7: No system battery
423               0xff: Unknown
424            6) Remaining battery life (percentage of charge):
425               0-100: valid
426               -1: Unknown
427            7) Remaining battery life (time units):
428               Number of remaining minutes or seconds
429               -1: Unknown
430            8) min = minutes; sec = seconds */
431
432         unsigned short  ac_line_status;
433         unsigned short  battery_status = 0;
434         unsigned short  battery_flag   = 0xff;
435         int             percentage     = -1;
436         int             time_units     = -1;
437         int             real_count     = 0;
438         int             i;
439         char *          p = buf;
440         char            charging       = 0;
441         long            charge         = -1;
442         long            amperage       = 0;
443         unsigned long   btype          = 0;
444
445         ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
446         for (i=0; i<pmu_battery_count; i++) {
447                 if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
448                         battery_status++;
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 (0 == battery_status)
465                 ac_line_status = 1;
466         battery_status = 0xff;
467         if (real_count) {
468                 if (amperage < 0) {
469                         if (btype == PMU_BATT_TYPE_SMART)
470                                 time_units = (charge * 59) / (amperage * -1);
471                         else
472                                 time_units = (charge * 16440) / (amperage * -60);
473                 }
474                 percentage /= real_count;
475                 if (charging > 0) {
476                         battery_status = 0x03;
477                         battery_flag = 0x08;
478                 } else if (percentage <= APM_CRITICAL) {
479                         battery_status = 0x02;
480                         battery_flag = 0x04;
481                 } else if (percentage <= APM_LOW) {
482                         battery_status = 0x01;
483                         battery_flag = 0x02;
484                 } else {
485                         battery_status = 0x00;
486                         battery_flag = 0x01;
487                 }
488         }
489         p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
490                      driver_version,
491                      (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
492                      FAKE_APM_BIOS_VERSION & 0xff,
493                      0,
494                      ac_line_status,
495                      battery_status,
496                      battery_flag,
497                      percentage,
498                      time_units,
499                      "min");
500
501         return p - buf;
502 }
503
504 static const struct file_operations apm_bios_fops = {
505         .owner          = THIS_MODULE,
506         .read           = do_read,
507         .poll           = do_poll,
508         .ioctl          = do_ioctl,
509         .open           = do_open,
510         .release        = do_release,
511 };
512
513 static struct miscdevice apm_device = {
514         APM_MINOR_DEV,
515         "apm_bios",
516         &apm_bios_fops
517 };
518
519 static int __init apm_emu_init(void)
520 {
521         struct proc_dir_entry *apm_proc;
522
523         if (sys_ctrler != SYS_CTRLER_PMU) {
524                 printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
525                 return -ENODEV;
526         }
527                 
528         apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
529         if (apm_proc)
530                 apm_proc->owner = THIS_MODULE;
531
532         if (misc_register(&apm_device) != 0)
533                 printk(KERN_INFO "Could not create misc. device for apm\n");
534
535         pmu_register_sleep_notifier(&apm_sleep_notifier);
536
537         printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
538
539         return 0;
540 }
541
542 static void __exit apm_emu_exit(void)
543 {
544         pmu_unregister_sleep_notifier(&apm_sleep_notifier);
545         misc_deregister(&apm_device);
546         remove_proc_entry("apm", NULL);
547
548         printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
549 }
550
551 module_init(apm_emu_init);
552 module_exit(apm_emu_exit);
553
554 MODULE_AUTHOR("Benjamin Herrenschmidt");
555 MODULE_DESCRIPTION("APM emulation layer for PowerMac");
556 MODULE_LICENSE("GPL");
557