Commit 871392ae authored by Russell King's avatar Russell King

[ARM] Revive kapmd and provide apm_queue_event()

Add kapmd thread to provide a process context to handle "APM"
events submitted via apm_queue_event().

Add apm_queue_event(), which can be called from hardware
interrupt handlers and the like, typically to fire off a
suspend.
parent 2524d90d
...@@ -91,10 +91,15 @@ static DECLARE_RWSEM(user_list_lock); ...@@ -91,10 +91,15 @@ static DECLARE_RWSEM(user_list_lock);
static LIST_HEAD(apm_user_list); static LIST_HEAD(apm_user_list);
/* /*
* The kapmd info. * kapmd info. kapmd provides us a process context to handle
* "APM" events within - specifically necessary if we're going
* to be suspending the system.
*/ */
static struct task_struct *kapmd; static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
static DECLARE_COMPLETION(kapmd_exit); static DECLARE_COMPLETION(kapmd_exit);
static spinlock_t kapmd_queue_lock = SPIN_LOCK_UNLOCKED;
static struct apm_queue kapmd_queue;
static const char driver_version[] = "1.13"; /* no spaces */ static const char driver_version[] = "1.13"; /* no spaces */
...@@ -454,34 +459,53 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length) ...@@ -454,34 +459,53 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length)
} }
#endif #endif
#if 0 static int kapmd(void *arg)
static int kapmd(void *startup)
{ {
struct task_struct *tsk = current; daemonize("kapmd");
current->flags |= PF_NOFREEZE;
daemonize(); do {
strcpy(tsk->comm, "kapmd"); apm_event_t event;
kapmd = tsk;
spin_lock_irq(&tsk->sigmask_lock); wait_event_interruptible(kapmd_wait,
siginitsetinv(&tsk->blocked, sigmask(SIGQUIT)); !queue_empty(&kapmd_queue) || !pm_active);
recalc_sigpending(tsk);
spin_unlock_irq(&tsk->sigmask_lock);
complete((struct completion *)startup); if (!pm_active)
break;
do { spin_lock_irq(&kapmd_queue_lock);
set_task_state(tsk, TASK_INTERRUPTIBLE); event = 0;
schedule(); if (!queue_empty(&kapmd_queue))
} while (!signal_pending(tsk)); event = queue_get_event(&kapmd_queue);
spin_unlock_irq(&kapmd_queue_lock);
switch (event) {
case 0:
break;
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
queue_event(event, NULL);
break;
case APM_USER_SUSPEND:
case APM_SYS_SUSPEND:
queue_event(event, NULL);
if (suspends_pending == 0)
apm_suspend();
break;
case APM_CRITICAL_SUSPEND:
apm_suspend();
break;
}
} while (1);
complete_and_exit(&kapmd_exit, 0); complete_and_exit(&kapmd_exit, 0);
} }
#endif
static int __init apm_init(void) static int __init apm_init(void)
{ {
// struct completion startup = COMPLETION_INITIALIZER(startup);
int ret; int ret;
if (apm_disabled) { if (apm_disabled) {
...@@ -494,22 +518,24 @@ static int __init apm_init(void) ...@@ -494,22 +518,24 @@ static int __init apm_init(void)
return -EINVAL; return -EINVAL;
} }
// ret = kernel_thread(kapmd, &startup, CLONE_FS | CLONE_FILES);
// if (ret)
// return ret;
// wait_for_completion(&startup);
pm_active = 1; pm_active = 1;
ret = kernel_thread(kapmd, NULL, CLONE_KERNEL);
if (ret < 0) {
pm_active = 0;
return ret;
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
create_proc_info_entry("apm", 0, NULL, apm_get_info); create_proc_info_entry("apm", 0, NULL, apm_get_info);
#endif #endif
ret = misc_register(&apm_device); ret = misc_register(&apm_device);
if (ret != 0) { if (ret != 0) {
pm_active = 0;
remove_proc_entry("apm", NULL); remove_proc_entry("apm", NULL);
send_sig(SIGQUIT, kapmd, 1);
pm_active = 0;
wake_up(&kapmd_wait);
wait_for_completion(&kapmd_exit); wait_for_completion(&kapmd_exit);
} }
...@@ -520,9 +546,10 @@ static void __exit apm_exit(void) ...@@ -520,9 +546,10 @@ static void __exit apm_exit(void)
{ {
misc_deregister(&apm_device); misc_deregister(&apm_device);
remove_proc_entry("apm", NULL); remove_proc_entry("apm", NULL);
pm_active = 0; pm_active = 0;
// send_sig(SIGQUIT, kapmd, 1); wake_up(&kapmd_wait);
// wait_for_completion(&kapmd_exit); wait_for_completion(&kapmd_exit);
} }
module_init(apm_init); module_init(apm_init);
...@@ -549,3 +576,27 @@ static int __init apm_setup(char *str) ...@@ -549,3 +576,27 @@ static int __init apm_setup(char *str)
__setup("apm=", apm_setup); __setup("apm=", apm_setup);
#endif #endif
/**
* apm_queue_event - queue an APM event for kapmd
* @event: APM event
*
* Queue an APM event for kapmd to process and ultimately take the
* appropriate action. Only a subset of events are handled:
* %APM_LOW_BATTERY
* %APM_POWER_STATUS_CHANGE
* %APM_USER_SUSPEND
* %APM_SYS_SUSPEND
* %APM_CRITICAL_SUSPEND
*/
void apm_queue_event(apm_event_t event)
{
unsigned long flags;
spin_lock_irqsave(&kapmd_queue_lock, flags);
queue_add_event(&kapmd_queue, event);
spin_unlock_irqrestore(&kapmd_queue_lock, flags);
wake_up_interruptible(&kapmd_wait);
}
EXPORT_SYMBOL(apm_queue_event);
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define ARM_ASM_SA1100_APM_H #define ARM_ASM_SA1100_APM_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/apm_bios.h>
/* /*
* This structure gets filled in by the machine specific 'get_power_status' * This structure gets filled in by the machine specific 'get_power_status'
...@@ -56,5 +57,9 @@ struct apm_power_info { ...@@ -56,5 +57,9 @@ struct apm_power_info {
*/ */
extern void (*apm_get_power_status)(struct apm_power_info *); extern void (*apm_get_power_status)(struct apm_power_info *);
/*
* Queue an event (APM_SYS_SUSPEND or APM_CRITICAL_SUSPEND)
*/
void apm_queue_event(apm_event_t event);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment