@@ -2,7 +2,7 @@
* drivers/input/touchscreen/sweep2wake.c
*
*
* Copyright (c) 2012 , Dennis Rassmann <showp1984@gmail.com>
* Copyright (c) 2013 , Dennis Rassmann <showp1984@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,9 +26,34 @@
#include < linux/init.h>
#include < linux/err.h>
#include < linux/input/sweep2wake.h>
#include < linux/slab.h>
#include < linux/workqueue.h>
#include < linux/input.h>
#ifndef CONFIG_HAS_EARLYSUSPEND
#include < linux/lcd_notify.h>
#else
#include < linux/earlysuspend.h>
#endif
#include < linux/hrtimer.h>
/* uncomment since no touchscreen defines android touch, do that here */
// #define ANDROID_TOUCH_DECLARED
/* Version, author, desc, etc */
#define DRIVER_AUTHOR " Dennis Rassmann <showp1984@gmail.com>"
#define DRIVER_DESCRIPTION " Sweep2wake for almost any device"
#define DRIVER_VERSION " 1.5"
#define LOGTAG " [sweep2wake]: "
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_DESCRIPTION);
MODULE_VERSION (DRIVER_VERSION);
MODULE_LICENSE (" GPLv2" );
/* Tuneables */
#define DEBUG 0
#define S2W_DEBUG 0
#define S2W_DEFAULT 0
#define S2W_S2SONLY_DEFAULT 0
#define S2W_PWRKEY_DUR 60
#define S2W_Y_MAX 1079
@@ -40,35 +65,35 @@
/* Resources */
int s2w_switch = 1 ;
bool scr_suspended = false , exec_count = true , irq_wake = false ;
bool scr_on_touch = false , barrier[2 ] = {false , false };
int s2w_switch = S2W_DEFAULT, s2w_s2sonly = S2W_S2SONLY_DEFAULT;
static int touch_x = 0 , touch_y = 0 ;
static bool touch_x_called = false , touch_y_called = false ;
static bool scr_suspended = false , exec_count = true ;
static bool scr_on_touch = false , barrier[2 ] = {false , false };
#ifndef CONFIG_HAS_EARLYSUSPEND
static struct notifier_block s2w_lcd_notif;
#endif
static struct input_dev * sweep2wake_pwrdev;
static DEFINE_MUTEX (pwrkeyworklock);
static struct workqueue_struct *s2w_input_wq;
static struct work_struct s2w_input_work;
/* Read cmdline for s2w */
static int __init read_s2w_cmdline (char *s2w)
{
if (strcmp (s2w, " 1" ) == 0 ) {
printk (KERN_INFO " [cmdline_s2w]: Sweep2Wake enabled. | s2w='%s '" , s2w);
pr_info ( " [cmdline_s2w]: Sweep2Wake enabled. | s2w='%s '\n " , s2w);
s2w_switch = 1 ;
} else if (strcmp (s2w, " 0" ) == 0 ) {
printk (KERN_INFO " [cmdline_s2w]: Sweep2Wake disabled. | s2w='%s '" , s2w);
pr_info ( " [cmdline_s2w]: Sweep2Wake disabled. | s2w='%s '\n " , s2w);
s2w_switch = 0 ;
} else {
printk (KERN_INFO " [cmdline_s2w]: No valid input found. Going with default: | s2w='%u '" , s2w_switch);
pr_info ( " [cmdline_s2w]: No valid input found. Going with default: | s2w='%u '\n " , s2w_switch);
}
return 1 ;
}
__setup (" s2w=" , read_s2w_cmdline);
/* PowerKey setter */
void sweep2wake_setdev (struct input_dev * input_device) {
sweep2wake_pwrdev = input_device;
return ;
}
EXPORT_SYMBOL (sweep2wake_setdev);
/* PowerKey work func */
static void sweep2wake_presspwr (struct work_struct * sweep2wake_presspwr_work) {
if (!mutex_trylock (&pwrkeyworklock))
@@ -85,30 +110,30 @@ static void sweep2wake_presspwr(struct work_struct * sweep2wake_presspwr_work) {
static DECLARE_WORK (sweep2wake_presspwr_work, sweep2wake_presspwr);
/* PowerKey trigger */
void sweep2wake_pwrtrigger (void ) {
static void sweep2wake_pwrtrigger (void ) {
schedule_work (&sweep2wake_presspwr_work);
return ;
}
/* reset on finger release */
void sweep2wake_reset (void ) {
static void sweep2wake_reset (void ) {
exec_count = true ;
barrier[0 ] = false ;
barrier[1 ] = false ;
scr_on_touch = false ;
}
/* Sweep2wake main function */
void detect_sweep2wake (int x, int y, bool st)
static void detect_sweep2wake (int x, int y, bool st)
{
int prevx = 0 , nextx = 0 ;
bool single_touch = st;
#if DEBUG
pr_info (" [sweep2wake]: x,y(%4d ,%4d ) single:%s \n " ,
#if S2W_DEBUG
pr_info (LOGTAG " x,y(%4d ,%4d ) single:%s \n " ,
x, y, (single_touch) ? " true" : " false" );
#endif
// left->right
if ((single_touch) && (scr_suspended == true ) && (s2w_switch > 0 )) {
if ((single_touch) && (scr_suspended == true ) && (s2w_switch > 0 && !s2w_s2sonly )) {
prevx = 0 ;
nextx = S2W_X_B1;
if ((barrier[0 ] == true ) ||
@@ -128,7 +153,7 @@ void detect_sweep2wake(int x, int y, bool st)
(y > 0 )) {
if (x > (S2W_X_MAX - S2W_X_FINAL)) {
if (exec_count) {
printk (KERN_INFO " [sweep2wake]: ON " );
pr_info (LOGTAG " ON \n " );
sweep2wake_pwrtrigger ();
exec_count = false ;
}
@@ -158,7 +183,7 @@ void detect_sweep2wake(int x, int y, bool st)
(y > S2W_Y_LIMIT)) {
if (x < S2W_X_FINAL) {
if (exec_count) {
printk (KERN_INFO " [sweep2wake]: OFF" );
pr_info (LOGTAG " OFF\n " );
sweep2wake_pwrtrigger ();
exec_count = false ;
}
@@ -169,24 +194,299 @@ void detect_sweep2wake(int x, int y, bool st)
}
}
static void s2w_input_callback (struct work_struct *unused) {
detect_sweep2wake (touch_x, touch_y, true );
return ;
}
static void s2w_input_event (struct input_handle *handle, unsigned int type,
unsigned int code, int value) {
#if S2W_DEBUG
pr_info (" sweep2wake: code: %s |%u , val: %i \n " ,
((code==ABS_MT_POSITION_X) ? " X" :
(code==ABS_MT_POSITION_Y) ? " Y" :
(code==ABS_MT_TRACKING_ID) ? " ID" :
" undef" ), code, value);
#endif
if (code == ABS_MT_SLOT) {
sweep2wake_reset ();
return ;
}
if (code == ABS_MT_TRACKING_ID && value == -1 ) {
sweep2wake_reset ();
return ;
}
if (code == ABS_MT_POSITION_X) {
touch_x = value;
touch_x_called = true ;
}
if (code == ABS_MT_POSITION_Y) {
touch_y = value;
touch_y_called = true ;
}
if (touch_x_called && touch_y_called) {
touch_x_called = false ;
touch_y_called = false ;
queue_work_on (0 , s2w_input_wq, &s2w_input_work);
}
}
static int input_dev_filter (struct input_dev *dev) {
if (strstr (dev->name , " touch" )) {
return 0 ;
} else {
return 1 ;
}
}
static int s2w_input_connect (struct input_handler *handler,
struct input_dev *dev, const struct input_device_id *id) {
struct input_handle *handle;
int error;
if (input_dev_filter (dev))
return -ENODEV;
handle = kzalloc (sizeof (struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = " s2w" ;
error = input_register_handle (handle);
if (error)
goto err2;
error = input_open_device (handle);
if (error)
goto err1;
return 0 ;
err1:
input_unregister_handle (handle);
err2:
kfree (handle);
return error;
}
static void s2w_input_disconnect (struct input_handle *handle) {
input_close_device (handle);
input_unregister_handle (handle);
kfree (handle);
}
static const struct input_device_id s2w_ids[] = {
{ .driver_info = 1 },
{ },
};
static struct input_handler s2w_input_handler = {
.event = s2w_input_event,
.connect = s2w_input_connect,
.disconnect = s2w_input_disconnect,
.name = " s2w_inputreq" ,
.id_table = s2w_ids,
};
#ifndef CONFIG_HAS_EARLYSUSPEND
static int lcd_notifier_callback (struct notifier_block *this,
unsigned long event, void *data)
{
switch (event) {
case LCD_EVENT_ON_END:
scr_suspended = false ;
break ;
case LCD_EVENT_OFF_END:
scr_suspended = true ;
break ;
default :
break ;
}
return 0 ;
}
#else
static void s2w_early_suspend (struct early_suspend *h) {
scr_suspended = true ;
}
static void s2w_late_resume (struct early_suspend *h) {
scr_suspended = false ;
}
static struct early_suspend s2w_early_suspend_handler = {
.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN,
.suspend = s2w_early_suspend,
.resume = s2w_late_resume,
};
#endif
/*
* INIT / EXIT stuff below here
* SYSFS stuff below here
*/
static ssize_t s2w_sweep2wake_show (struct device *dev,
struct device_attribute *attr, char *buf)
{
size_t count = 0 ;
count += sprintf (buf, " %d \n " , s2w_switch);
return count;
}
static ssize_t s2w_sweep2wake_dump (struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
if (buf[0 ] >= ' 0' && buf[0 ] <= ' 1' && buf[1 ] == ' \n ' )
if (s2w_switch != buf[0 ] - ' 0' )
s2w_switch = buf[0 ] - ' 0' ;
return count;
}
static DEVICE_ATTR (sweep2wake, (S_IWUSR|S_IRUGO),
s2w_sweep2wake_show, s2w_sweep2wake_dump);
static ssize_t s2w_s2w_s2sonly_show (struct device *dev,
struct device_attribute *attr, char *buf)
{
size_t count = 0 ;
count += sprintf (buf, " %d \n " , s2w_s2sonly);
return count;
}
static ssize_t s2w_s2w_s2sonly_dump (struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
if (buf[0 ] >= ' 0' && buf[0 ] <= ' 1' && buf[1 ] == ' \n ' )
if (s2w_s2sonly != buf[0 ] - ' 0' )
s2w_s2sonly = buf[0 ] - ' 0' ;
return count;
}
static DEVICE_ATTR (s2w_s2sonly, (S_IWUSR|S_IRUGO),
s2w_s2w_s2sonly_show, s2w_s2w_s2sonly_dump);
static ssize_t s2w_version_show (struct device *dev,
struct device_attribute *attr, char *buf)
{
size_t count = 0 ;
count += sprintf (buf, " %s \n " , DRIVER_VERSION);
return count;
}
static ssize_t s2w_version_dump (struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
static DEVICE_ATTR (sweep2wake_version, (S_IWUSR|S_IRUGO),
s2w_version_show, s2w_version_dump);
/*
* INIT / EXIT stuff below here
*/
#ifdef ANDROID_TOUCH_DECLARED
extern struct kobject *android_touch_kobj;
#else
struct kobject *android_touch_kobj;
EXPORT_SYMBOL_GPL (android_touch_kobj);
#endif
static int __init sweep2wake_init (void )
{
pr_info (" [sweep2wake]: %s done\n " , __func__);
int rc = 0 ;
sweep2wake_pwrdev = input_allocate_device ();
if (!sweep2wake_pwrdev) {
pr_err (" Can't allocate suspend autotest power button\n " );
goto err_alloc_dev;
}
input_set_capability (sweep2wake_pwrdev, EV_KEY, KEY_POWER);
sweep2wake_pwrdev->name = " s2w_pwrkey" ;
sweep2wake_pwrdev->phys = " s2w_pwrkey/input0" ;
rc = input_register_device (sweep2wake_pwrdev);
if (rc) {
pr_err (" %s : input_register_device err=%d \n " , __func__, rc);
goto err_input_dev;
}
s2w_input_wq = create_workqueue (" s2wiwq" );
if (!s2w_input_wq) {
pr_err (" %s : Failed to create s2wiwq workqueue\n " , __func__);
return -EFAULT;
}
INIT_WORK (&s2w_input_work, s2w_input_callback);
rc = input_register_handler (&s2w_input_handler);
if (rc)
pr_err (" %s : Failed to register s2w_input_handler\n " , __func__);
#ifndef CONFIG_HAS_EARLYSUSPEND
s2w_lcd_notif.notifier_call = lcd_notifier_callback;
if (lcd_register_client (&s2w_lcd_notif) != 0 ) {
pr_err (" %s : Failed to register lcd callback\n " , __func__);
}
#else
register_early_suspend (&s2w_early_suspend_handler);
#endif
#ifndef ANDROID_TOUCH_DECLARED
android_touch_kobj = kobject_create_and_add (" android_touch" , NULL ) ;
if (android_touch_kobj == NULL ) {
pr_warn (" %s : android_touch_kobj create_and_add failed\n " , __func__);
}
#endif
rc = sysfs_create_file (android_touch_kobj, &dev_attr_sweep2wake.attr );
if (rc) {
pr_warn (" %s : sysfs_create_file failed for sweep2wake\n " , __func__);
}
rc = sysfs_create_file (android_touch_kobj, &dev_attr_s2w_s2sonly.attr );
if (rc) {
pr_warn (" %s : sysfs_create_file failed for s2w_s2sonly\n " , __func__);
}
rc = sysfs_create_file (android_touch_kobj, &dev_attr_sweep2wake_version.attr );
if (rc) {
pr_warn (" %s : sysfs_create_file failed for sweep2wake_version\n " , __func__);
}
err_input_dev:
input_free_device (sweep2wake_pwrdev);
err_alloc_dev:
pr_info (LOGTAG" %s done\n " , __func__);
return 0 ;
}
static void __exit sweep2wake_exit (void )
{
#ifndef ANDROID_TOUCH_DECLARED
kobject_del (android_touch_kobj);
#endif
#ifndef CONFIG_HAS_EARLYSUSPEND
lcd_unregister_client (&s2w_lcd_notif);
#endif
input_unregister_handler (&s2w_input_handler);
destroy_workqueue (s2w_input_wq);
input_unregister_device (sweep2wake_pwrdev);
input_free_device (sweep2wake_pwrdev);
return ;
}
module_init (sweep2wake_init);
module_exit (sweep2wake_exit);
MODULE_DESCRIPTION (" Sweep2wake" );
MODULE_LICENSE (" GPLv2" );