# # Patch managed by http://www.holgerschurig.de/patcher.html # --- linux-2.4.17_mvl21/arch/arm/mach-sa1100/apm.c~apm-hh-merge +++ linux-2.4.17_mvl21/arch/arm/mach-sa1100/apm.c @@ -86,6 +86,8 @@ int magic; struct apm_user * next; int suser: 1; + int writer : 1; + int reader : 1; int suspend_wait: 1; int suspend_result; int suspends_pending; @@ -105,7 +107,7 @@ /* * Local variables */ -//static int suspends_pending; +static int suspends_pending; //static int standbys_pending; //static int ignore_normal_resume; @@ -123,8 +125,6 @@ #else static int power_off = 1; #endif -static int exit_kapmd; -static int kapmd_running; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); @@ -192,6 +192,41 @@ return as->events[as->event_tail]; } +static void queue_event(apm_event_t event, struct apm_user *sender) +{ + struct apm_user * as; + if (user_list == NULL) + return; + for (as = user_list; as != NULL; as = as->next) { + if ((as == sender) || (!as->reader)) + continue; + as->event_head = (as->event_head + 1) % APM_MAX_EVENTS; + if (as->event_head == as->event_tail) { + static int notified; + + if (notified++ == 0) + printk(KERN_ERR "apm: an event queue overflowed\n"); + as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; + } + as->events[as->event_head] = event; + if ((!as->suser) || (!as->writer)) + continue; + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + as->suspends_pending++; + suspends_pending++; + break; + + case APM_SYS_STANDBY: + case APM_USER_STANDBY: + as->standbys_pending++; + break; + } + } + wake_up_interruptible(&apm_waitqueue); +} + static int check_apm_user(struct apm_user *as, const char *func) { if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { @@ -270,7 +305,6 @@ return 0; } -extern int pm_do_suspend(void); static int do_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) @@ -284,7 +318,17 @@ return -EPERM; switch (cmd) { case APM_IOC_SUSPEND: - pm_do_suspend(); + if (as->suspends_read > 0) { + as->suspends_read--; + as->suspends_pending--; + suspends_pending--; + } else { + queue_event(APM_USER_SUSPEND, as); + } + + if (suspends_pending <= 0) + wake_up(&apm_suspend_waitqueue); + break; default: return -EINVAL; @@ -301,6 +345,20 @@ return 0; filp->private_data = NULL; lock_kernel(); + if (user_list == as) + user_list = as->next; + else { + struct apm_user * as1; + + for (as1 = user_list; + (as1 != NULL) && (as1->next != as); + as1 = as1->next) + ; + if (as1 == NULL) + printk(KERN_ERR "apm: filp not in user list\n"); + else + as1->next = as->next; + } unlock_kernel(); kfree(as); return 0; @@ -328,6 +386,8 @@ * privileged operation -- cevans */ as->suser = capable(CAP_SYS_ADMIN); + as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; + as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; as->next = user_list; user_list = as; filp->private_data = as; @@ -411,33 +471,7 @@ return p - buf; } -#ifndef MODULE -static int __init apm_setup(char *str) -{ - int invert; - - while ((str != NULL) && (*str != '\0')) { - if (strncmp(str, "off", 3) == 0) - apm_disabled = 1; - if (strncmp(str, "on", 2) == 0) - apm_disabled = 0; - invert = (strncmp(str, "no-", 3) == 0); - if (invert) - str += 3; - if (strncmp(str, "debug", 5) == 0) - debug = !invert; - if ((strncmp(str, "power-off", 9) == 0) || - (strncmp(str, "power_off", 9) == 0)) - power_off = !invert; - str = strchr(str, ','); - if (str != NULL) - str += strspn(str, ", \t"); - } - return 1; -} -__setup("apm=", apm_setup); -#endif static struct file_operations apm_bios_fops = { owner: THIS_MODULE, @@ -449,13 +483,55 @@ }; static struct miscdevice apm_device = { - APM_MINOR_DEV, - "apm_bios", - &apm_bios_fops + minor : APM_MINOR_DEV, + name : "apm_bios", + fops : &apm_bios_fops }; #define APM_INIT_ERROR_RETURN return -1 +static pid_t apmd_pid; +static DECLARE_COMPLETION(apmd_exited); + +static int apm(void *unused) +{ + unsigned short bx; + unsigned short cx; + unsigned short dx; + int error; + char * power_stat; + char * bat_stat; + DECLARE_WAITQUEUE(wait, current); + struct apm_user au, *as; + + lock_kernel(); + + daemonize(); + + strcpy(current->comm, "kapmd"); + + as = &au; + as->magic = APM_BIOS_MAGIC; + as->event_tail = as->event_head = 0; + as->suspends_pending = as->standbys_pending = 0; + as->suspends_read = as->standbys_read = 0; + as->suser = 1; + as->writer = 1; + as->reader = 0; + + while (!signal_pending (current)) { + interruptible_sleep_on(&apm_suspend_waitqueue); + + pm_suggest_suspend(); + + queue_event(APM_NORMAL_RESUME, as); + } + + unlock_kernel(); + + complete_and_exit(&apmd_exited, 0); +} + /* * Just start the APM thread. We do NOT want to do APM BIOS * calls from anything but the APM thread, if for no other reason @@ -494,18 +570,19 @@ misc_register(&apm_device); + apmd_pid = kernel_thread(apm, NULL, 0); + return 0; } static void __exit apm_exit(void) { misc_deregister(&apm_device); - remove_proc_entry("apm", NULL); + remove_proc_entry("apm", NULL); + kill_proc (apmd_pid, SIGTERM, 1); + wait_for_completion(&apmd_exited); if (power_off) pm_power_off = NULL; - exit_kapmd = 1; - while (kapmd_running) - schedule(); pm_active = 0; } @@ -514,6 +591,7 @@ MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell"); MODULE_DESCRIPTION("A minimal emulation of APM"); +MODULE_LICENSE("GPL"); MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Enable debug mode"); MODULE_PARM(power_off, "i"); --- linux-2.4.17_mvl21/arch/arm/mach-sa1100/pm.c~apm-hh-merge +++ linux-2.4.17_mvl21/arch/arm/mach-sa1100/pm.c @@ -53,6 +53,10 @@ #include #endif +#define __KERNEL_SYSCALLS__ +#include + + /* * ARGH! Stupid ACPI people. They should define this in linux/sysctl.h, * NOT linux/acpi.h. @@ -64,123 +68,6 @@ #define CTL_ACPI 9999 #define ACPI_S1_SLP_TYP 19 -#ifndef CONFIG_SA1100_BEAGLE - -extern void sa1100_cpu_suspend(void); -extern void sa1100_cpu_resume(void); - -extern unsigned long *sleep_save; /* virtual address */ -extern unsigned long sleep_save_p; /* physical address */ - -#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x -#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] - -int sa1110_suspend(void) -{ - int retval; - - /* set up pointer to sleep parameters */ - sleep_save = kmalloc (SLEEP_SAVE_SIZE*sizeof(long), GFP_ATOMIC); - if (!sleep_save) - return -ENOMEM; - sleep_save_p = virt_to_phys(sleep_save); - - retval = pm_send_all(PM_SUSPEND, (void *)2); - if (retval) { - kfree(sleep_save); - return retval; - } - - cli(); - - /* preserve current time */ - RCNR = xtime.tv_sec; - - /* save vital registers */ - SAVE(OSCR); - SAVE(OSMR0); - SAVE(OSMR1); - SAVE(OSMR2); - SAVE(OSMR3); - SAVE(OIER); - - SAVE(GPDR); - SAVE(GRER); - SAVE(GFER); - SAVE(GAFR); - - SAVE(PPDR); - SAVE(PPSR); - SAVE(PPAR); - SAVE(PSDR); - - SAVE(Ser1SDCR0); - - SAVE(ICMR); - - /* ... maybe a global variable initialized by arch code to set this? */ - GRER = PWER; - GFER = 0; - GEDR = GEDR; - - /* Clear previous reset status */ - RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR; - - /* set resume return address */ - PSPR = virt_to_phys(sa1100_cpu_resume); - - /* go zzz */ - sa1100_cpu_suspend(); - - /* ensure not to come back here if it wasn't intended */ - PSPR = 0; - - DPRINTK("*** made it back from resume\n"); - - /* restore registers */ - RESTORE(GPDR); - RESTORE(GRER); - RESTORE(GFER); - RESTORE(GAFR); - - /* clear any edge detect bit */ - GEDR = GEDR; - - RESTORE(PPDR); - RESTORE(PPSR); - RESTORE(PPAR); - RESTORE(PSDR); - - RESTORE(Ser1SDCR0); - - PSSR = PSSR_PH; - - RESTORE(OSMR0); - RESTORE(OSMR1); - RESTORE(OSMR2); - RESTORE(OSMR3); - RESTORE(OSCR); - RESTORE(OIER); - - ICLR = 0; - ICCR = 1; - RESTORE(ICMR); - - /* restore current time */ - xtime.tv_sec = RCNR; - - sti(); - - kfree (sleep_save); - -#ifdef CONFIG_CPU_FREQ - cpufreq_restore(); -#endif - - return pm_send_all(PM_RESUME, (void *)0); -} - -#else //CONFIG_SA1100_BEAGLE typedef struct _tag_SLEEP_SAVED_DATA { uint wakeup_addr; @@ -363,9 +250,6 @@ " ); } -extern void h3600_control_egpio( enum ipaq_egpio_type x, int setp ); -extern unsigned long h3600_read_egpio( void ); - static int GPDR_saved; static int GPLR_saved; static int GRER_saved; @@ -742,21 +626,37 @@ Ser3UTSR1 = 0xff; } -#endif //CONFIG_SA1100_BEAGLE - +/* + * If pm_suggest_suspend_hook is non-NULL, it is called by pm_suggest_suspend. + * + * If sysctl_pm_do_suspend_hook is non-NULL, it is called by sysctl_pm_do_suspend. + * If it returns a true value, then pm_suspend is not called. + * Use this to hook in apmd, for now. + * + * -not exported just so that the code compiles + */ +int (*pm_suggest_suspend_hook)(int state); +int (*pm_sysctl_suspend_hook)(int state); +int pm_use_sbin_pm_helper = 1; static char pm_helper_path[128] = "/sbin/pm_helper"; +extern int exec_usermodehelper(char *path, char **argv, char **envp); +int debug_pm = 0; +static int pm_helper_veto = 0; -static void +static int run_sbin_pm_helper( pm_request_t action ) { int i; char *argv[3], *envp[8]; if (!pm_helper_path[0]) - return; + return 2; if ( action != PM_SUSPEND && action != PM_RESUME ) - return; + return 1; + + /* Be root */ + current->uid = current->gid = 0; i = 0; argv[i++] = pm_helper_path; @@ -771,14 +671,15 @@ envp[i] = 0; /* other stuff we want to pass to /sbin/hotplug */ - call_usermodehelper (argv [0], argv, envp); + return exec_usermodehelper (argv [0], argv, envp); } +int pm_force_suspend(void); + int pm_do_suspend(void) { - DPRINTK("suggest\n"); - run_sbin_pm_helper(PM_SUSPEND); - return 0; + DPRINTK("suspend now\n"); + return pm_force_suspend(); } #ifdef CONFIG_SA1100_BEAGLE @@ -863,9 +764,91 @@ } #endif +int pm_suggest_suspend(void) +{ + int retval; + + if (pm_suggest_suspend_hook) { + if (pm_suggest_suspend_hook(PM_SUSPEND)) + return 0; + } + + if (pm_use_sbin_pm_helper) { + pid_t pid; + int res; + int status = 0; + unsigned int old_fs; + + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_SUSPEND, 0 ); + if ( pid < 0 ) + return pid; + + if (debug_pm) + printk(KERN_CRIT "%s:%d got pid=%d\n", __FUNCTION__, __LINE__, pid); + + old_fs = get_fs (); + set_fs (get_ds ()); + res = waitpid(pid, &status, __WCLONE); + set_fs (old_fs); + + if ( pid != res ) { + if (debug_pm) + printk(KERN_CRIT ": waitpid returned %d (exit_code=%d); not suspending\n", res, status ); + + return -1; + } + + /*if ( WIFEXITED(status) && ( WIFEXITSTATUS(status) != 0 )) {*/ + if (( status & 0xff7f ) != 0 ) { + if (pm_helper_veto) { + if (debug_pm) + printk(KERN_CRIT "%s: SUSPEND WAS CANCELLED BY pm_helper (exit status %d)\n", __FUNCTION__, status >> 8); + return -1; + } else { + if (debug_pm) + printk(KERN_CRIT "%s: pm_helper returned %d, but going ahead anyway\n", __FUNCTION__, status >> 8); + } + } + } + + if (debug_pm) + printk(KERN_CRIT "%s: REALLY SUSPENDING NOW\n", __FUNCTION__ ); + + if (pm_sysctl_suspend_hook) { + if (pm_sysctl_suspend_hook(PM_SUSPEND)) + return 0; + } + + retval = pm_do_suspend(); + if (retval) { + if (debug_pm) + printk(KERN_CRIT "pm_suspend returned %d\n", retval); + return retval; + } + + if (pm_use_sbin_pm_helper) { + pid_t pid; + + if (debug_pm) + printk(KERN_CRIT "%s: running pm_helper for wakeup\n", __FUNCTION__); + + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_RESUME, 0 ); + if ( pid < 0 ) + return pid; + + if ( pid != waitpid ( pid, NULL, __WCLONE )) + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(pm_suggest_suspend); + + static struct ctl_table pm_table[] = { - {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0644, NULL, (proc_handler *)&pm_force_suspend}, +/* {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0644, NULL, (proc_handler *)&pm_force_suspend}, */ {2, "helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring}, #ifdef CONFIG_SA1100_BEAGLE {3, "wakeup_delayed_time", &wakeup_delayed_time, sizeof(wakeup_delayed_time), 0644, NULL, &proc_dointvec },