android_kernel_samsung_a7y1.../drivers/soc/samsung/state_notifier.c
2020-04-08 12:42:31 +02:00

135 lines
3.4 KiB
C

/*
* State Notifier Driver
*
* Copyright (c) 2013-2017, Pranav Vashi <neobuddy89@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/state_notifier.h>
#define DEFAULT_SUSPEND_DEFER_TIME 10
#define STATE_NOTIFIER "state_notifier"
/*
* debug = 1 will print all
*/
static unsigned int debug;
module_param_named(debug_mask, debug, uint, 0644);
#define dprintk(msg...) \
do { \
if (debug) \
pr_info(msg); \
} while (0)
static bool enabled = true;
module_param_named(enabled, enabled, bool, 0664);
static unsigned int suspend_defer_time = DEFAULT_SUSPEND_DEFER_TIME;
module_param_named(suspend_defer_time, suspend_defer_time, uint, 0664);
static struct delayed_work suspend_work;
static struct workqueue_struct *susp_wq;
struct work_struct resume_work;
bool state_suspended;
module_param_named(state_suspended, state_suspended, bool, 0444);
static bool suspend_in_progress;
static BLOCKING_NOTIFIER_HEAD(state_notifier_list);
/**
* state_register_client - register a client notifier
* @nb: notifier block to callback on events
*/
int state_register_client(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&state_notifier_list, nb);
}
EXPORT_SYMBOL(state_register_client);
/**
* state_unregister_client - unregister a client notifier
* @nb: notifier block to callback on events
*/
int state_unregister_client(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&state_notifier_list, nb);
}
EXPORT_SYMBOL(state_unregister_client);
/**
* state_notifier_call_chain - notify clients on state_events
* @val: Value passed unmodified to notifier function
* @v: pointer passed unmodified to notifier function
*
*/
int state_notifier_call_chain(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&state_notifier_list, val, v);
}
EXPORT_SYMBOL_GPL(state_notifier_call_chain);
static void _suspend_work(struct work_struct *work)
{
state_suspended = true;
state_notifier_call_chain(STATE_NOTIFIER_SUSPEND, NULL);
suspend_in_progress = false;
dprintk("%s: suspend completed.\n", STATE_NOTIFIER);
}
static void _resume_work(struct work_struct *work)
{
state_suspended = false;
state_notifier_call_chain(STATE_NOTIFIER_ACTIVE, NULL);
dprintk("%s: resume completed.\n", STATE_NOTIFIER);
}
void state_suspend(void)
{
dprintk("%s: suspend called.\n", STATE_NOTIFIER);
if (state_suspended || suspend_in_progress || !enabled)
return;
suspend_in_progress = true;
queue_delayed_work(susp_wq, &suspend_work,
msecs_to_jiffies(suspend_defer_time * 1000));
}
void state_resume(void)
{
dprintk("%s: resume called.\n", STATE_NOTIFIER);
if (delayed_work_pending(&suspend_work))
cancel_delayed_work_sync(&suspend_work);
suspend_in_progress = false;
if (state_suspended)
queue_work(susp_wq, &resume_work);
}
static int __init state_notifier_init(void)
{
susp_wq =
alloc_workqueue("state_susp_wq",
WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
if (!susp_wq)
pr_err("State Notifier failed to allocate suspend workqueue\n");
INIT_DELAYED_WORK(&suspend_work, _suspend_work);
INIT_WORK(&resume_work, _resume_work);
return 0;
}
subsys_initcall(state_notifier_init);
MODULE_AUTHOR("Pranav Vashi <neobuddy89@gmail.com>");
MODULE_DESCRIPTION("State Notifier Driver");
MODULE_LICENSE("GPLv2");