import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / wake_gesture_sensor / wake_gesture.c
diff --git a/drivers/misc/mediatek/wake_gesture_sensor/wake_gesture.c b/drivers/misc/mediatek/wake_gesture_sensor/wake_gesture.c
new file mode 100644 (file)
index 0000000..5991bcd
--- /dev/null
@@ -0,0 +1,622 @@
+#include "wake_gesture.h"
+
+static struct wag_context *wag_context_obj = NULL;
+
+static struct wag_init_info* wake_gesture_init= {0}; //modified
+static void wag_early_suspend(struct early_suspend *h);
+static void wag_late_resume(struct early_suspend *h);
+
+static int resume_enable_status = 0;
+static struct wake_lock wag_lock; 
+static void notify_wag_timeout(unsigned long);
+
+static struct wag_context *wag_context_alloc_object(void)
+{
+       struct wag_context *obj = kzalloc(sizeof(*obj), GFP_KERNEL); 
+       WAG_LOG("wag_context_alloc_object++++\n");
+       if(!obj)
+       {
+               WAG_ERR("Alloc wag object error!\n");
+               return NULL;
+       }       
+       atomic_set(&obj->wake, 0);
+       mutex_init(&obj->wag_op_mutex);
+
+       WAG_LOG("wag_context_alloc_object----\n");
+       return obj;
+}
+
+static void notify_wag_timeout(unsigned long data)
+{
+    wake_unlock(&wag_lock);
+}
+
+int wag_notify()
+{
+       int err=0;
+       int value=0;
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+       WAG_LOG("wag_notify++++\n");
+       
+       value =1;
+       input_report_rel(cxt->idev, EVENT_TYPE_WAG_VALUE, value);
+       input_sync(cxt->idev); 
+
+       wake_lock(&wag_lock);
+       mod_timer(&cxt->notify_timer, jiffies + HZ/5);
+       
+       return err;
+}
+
+static int wag_real_enable(int enable)
+{
+       int err =0;
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+       
+       if(WAG_RESUME == enable)
+       {
+               enable = resume_enable_status;
+       }
+       
+       if(1==enable)
+       {
+               resume_enable_status = 1;
+               if(atomic_read(&(wag_context_obj->early_suspend))) //not allow to enable under suspend
+               {
+                       return 0;
+               }
+               if(false==cxt->is_active_data)
+               {
+                       err = cxt->wag_ctl.open_report_data(1);
+                       if(err)
+                       { 
+                               err = cxt->wag_ctl.open_report_data(1);
+                               if(err)
+                               {
+                                       err = cxt->wag_ctl.open_report_data(1);
+                                       if(err)
+                                       {
+                                               WAG_ERR("enable_wake_gesture enable(%d) err 3 timers = %d\n", enable, err);
+                                               return err;
+                                       }
+                               }
+                       }
+                       cxt->is_active_data = true;
+                       WAG_LOG("enable_wake_gesture real enable  \n" );
+               }
+       }
+       else if((0==enable) || (WAG_SUSPEND == enable))
+       {
+       if(0==enable)
+                       resume_enable_status = 0;
+               if(true==cxt->is_active_data)
+               {
+                       err = cxt->wag_ctl.open_report_data(0);
+                       if(err)
+                       { 
+                               WAG_ERR("enable_wake_gestureenable(%d) err = %d\n", enable, err);
+                       }
+                       cxt->is_active_data =false;
+                       WAG_LOG("enable_wake_gesture real disable  \n" );
+               } 
+       }
+       return err;
+}
+
+int wag_enable_nodata(int enable)
+{
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+       if(NULL  == cxt->wag_ctl.open_report_data)
+       {
+               WAG_ERR("wag_enable_nodata:wag ctl path is NULL\n");
+               return -1;
+       }
+
+       if(1 == enable)
+       {
+               cxt->is_active_nodata = true;
+       }
+       if(0 == enable)
+       {
+               cxt->is_active_nodata = false;
+       }
+       wag_real_enable(enable);
+       return 0;
+}
+
+static ssize_t wag_show_enable_nodata(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+       
+       WAG_LOG("wag active: %d\n", cxt->is_active_nodata);
+       return snprintf(buf, PAGE_SIZE, "%d\n", cxt->is_active_nodata); 
+}
+
+static ssize_t wag_store_enable_nodata(struct device* dev, struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct wag_context *cxt = NULL;
+       WAG_LOG("wag_store_enable nodata buf=%s\n",buf);
+       mutex_lock(&wag_context_obj->wag_op_mutex);
+       cxt = wag_context_obj;
+       if(NULL == cxt->wag_ctl.open_report_data)
+       {
+               WAG_LOG("wag_ctl enable nodata NULL\n");
+               mutex_unlock(&wag_context_obj->wag_op_mutex);
+               return count;
+       }
+       if (!strncmp(buf, "1", 1))
+       {
+               wag_enable_nodata(1);
+       }
+       else if (!strncmp(buf, "0", 1))
+       {
+               wag_enable_nodata(0);
+       }
+       else
+       {
+               WAG_ERR(" wag_store enable nodata cmd error !!\n");
+       }
+       mutex_unlock(&wag_context_obj->wag_op_mutex);
+       return count;
+}
+
+static ssize_t wag_store_active(struct device* dev, struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct wag_context *cxt = NULL;
+       int res =0;
+       int en=0;
+       WAG_LOG("wag_store_active buf=%s\n",buf);
+       mutex_lock(&wag_context_obj->wag_op_mutex);
+       
+       cxt = wag_context_obj;
+       if((res = sscanf(buf, "%d", &en))!=1)
+       {
+               WAG_LOG(" wag_store_active param error: res = %d\n", res);
+       }
+       WAG_LOG(" wag_store_active en=%d\n",en);
+       if(1 == en)
+       {
+               wag_real_enable(1);
+       }
+       else if(0 == en)
+       {
+               wag_real_enable(0);
+       }
+       else
+       {
+               WAG_ERR(" wag_store_active error !!\n");
+       }
+       mutex_unlock(&wag_context_obj->wag_op_mutex);
+       WAG_LOG(" wag_store_active done\n");
+       return count;
+}
+/*----------------------------------------------------------------------------*/
+static ssize_t wag_show_active(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+
+       WAG_LOG("wag active: %d\n", cxt->is_active_data);
+       return snprintf(buf, PAGE_SIZE, "%d\n", cxt->is_active_data); 
+}
+
+static ssize_t wag_store_delay(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+
+static ssize_t wag_show_delay(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+
+static ssize_t wag_store_batch(struct device* dev, struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+static ssize_t wag_show_batch(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+static ssize_t wag_store_flush(struct device* dev, struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+static ssize_t wag_show_flush(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+    int len = 0;
+       WAG_LOG(" not support now\n");
+       return len;
+}
+
+static ssize_t wag_show_devnum(struct device* dev, 
+                                 struct device_attribute *attr, char *buf) 
+{
+       char *devname = NULL;
+       devname = dev_name(&wag_context_obj->idev->dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n", devname+5);  //TODO: why +5?
+}
+static int wake_gesture_remove(struct platform_device *pdev)
+{
+       WAG_LOG("wake_gesture_remove\n");
+       return 0;
+}
+
+static int wake_gesture_probe(struct platform_device *pdev) 
+{
+       WAG_LOG("wake_gesture_probe\n");
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id wake_gesture_of_match[] = {
+       { .compatible = "mediatek,wake_gesture", },
+       {},
+};
+#endif
+
+static struct platform_driver wake_gesture_driver = {
+       .probe      = wake_gesture_probe,
+       .remove     = wake_gesture_remove,    
+       .driver     = 
+       {
+               .name  = "wake_gesture",
+               #ifdef CONFIG_OF
+               .of_match_table = wake_gesture_of_match,
+               #endif
+       }
+};
+
+static int wag_real_driver_init(void) 
+{
+       int err=0;
+       WAG_LOG(" wag_real_driver_init +\n");
+       if(0 != wake_gesture_init)
+       {
+               WAG_LOG(" wag try to init driver %s\n", wake_gesture_init->name);
+               err = wake_gesture_init->init();
+               if(0 == err)
+               {
+                       WAG_LOG(" wag real driver %s probe ok\n", wake_gesture_init->name);
+               }
+       }
+       wake_lock_init(&wag_lock,WAKE_LOCK_SUSPEND,"wag wakelock");
+       init_timer(&wag_context_obj->notify_timer);
+       wag_context_obj->notify_timer.expires   = HZ/5; //200 ms
+       wag_context_obj->notify_timer.function  = notify_wag_timeout;
+       wag_context_obj->notify_timer.data      = (unsigned long)wag_context_obj;
+
+       return err;
+}
+
+int wag_driver_add(struct wag_init_info* obj) 
+{
+       int err=0;
+       
+       WAG_FUN();
+       WAG_LOG("register wake_gesture driver for the first time\n");
+       if(platform_driver_register(&wake_gesture_driver))
+       {
+               WAG_ERR("failed to register gensor driver already exist\n");
+       }
+       if(NULL == wake_gesture_init)
+       {
+               obj->platform_diver_addr = &wake_gesture_driver;
+               wake_gesture_init = obj;
+       }
+
+       if(NULL==wake_gesture_init)
+       {
+               WAG_ERR("WAG driver add err \n");
+               err=-1;
+       }
+       
+       return err;
+}
+EXPORT_SYMBOL_GPL(wag_driver_add);
+
+static int wag_misc_init(struct wag_context *cxt)
+{
+       int err=0;
+       cxt->mdev.minor = MISC_DYNAMIC_MINOR;
+       cxt->mdev.name  = WAG_MISC_DEV_NAME;
+       if((err = misc_register(&cxt->mdev)))
+       {
+               WAG_ERR("unable to register wag misc device!!\n");
+       }
+       return err;
+}
+
+static void wag_input_destroy(struct wag_context *cxt)
+{
+       struct input_dev *dev = cxt->idev;
+
+       input_unregister_device(dev);
+       input_free_device(dev);
+}
+
+static int wag_input_init(struct wag_context *cxt)
+{
+       struct input_dev *dev;
+       int err = 0;
+
+       dev = input_allocate_device();
+       if (NULL == dev)
+               return -ENOMEM;
+
+       dev->name = WAG_INPUTDEV_NAME;
+       input_set_capability(dev, EV_REL, EVENT_TYPE_WAG_VALUE);
+       
+       input_set_drvdata(dev, cxt);
+       set_bit(EV_REL, dev->evbit);
+       err = input_register_device(dev);
+       if (err < 0) {
+               input_free_device(dev);
+               return err;
+       }
+       cxt->idev= dev;
+
+       return 0;
+}
+
+DEVICE_ATTR(wagenablenodata,           S_IWUSR | S_IRUGO, wag_show_enable_nodata, wag_store_enable_nodata);
+DEVICE_ATTR(wagactive,                 S_IWUSR | S_IRUGO, wag_show_active, wag_store_active);
+DEVICE_ATTR(wagdelay,                  S_IWUSR | S_IRUGO, wag_show_delay,  wag_store_delay);
+DEVICE_ATTR(wagbatch,                  S_IWUSR | S_IRUGO, wag_show_batch,  wag_store_batch);
+DEVICE_ATTR(wagflush,                          S_IWUSR | S_IRUGO, wag_show_flush,  wag_store_flush);
+DEVICE_ATTR(wagdevnum,                         S_IWUSR | S_IRUGO, wag_show_devnum,  NULL);
+
+
+static struct attribute *wag_attributes[] = {
+       &dev_attr_wagenablenodata.attr,
+       &dev_attr_wagactive.attr,
+       &dev_attr_wagdelay.attr,
+       &dev_attr_wagbatch.attr,
+       &dev_attr_wagflush.attr,
+       &dev_attr_wagdevnum.attr,
+       NULL
+};
+
+static struct attribute_group wag_attribute_group = {
+       .attrs = wag_attributes
+};
+
+int wag_register_data_path(struct wag_data_path *data)
+{
+       struct wag_context *cxt = NULL;
+       cxt = wag_context_obj;
+       cxt->wag_data.get_data = data->get_data;
+       if(NULL == cxt->wag_data.get_data)
+       {
+               WAG_LOG("wag register data path fail \n");
+               return -1;
+       }
+       return 0;
+}
+
+int wag_register_control_path(struct wag_control_path *ctl)
+{
+       struct wag_context *cxt = NULL;
+       int err =0;
+       cxt = wag_context_obj;
+//     cxt->wag_ctl.enable = ctl->enable;
+//     cxt->wag_ctl.enable_nodata = ctl->enable_nodata;
+       cxt->wag_ctl.open_report_data = ctl->open_report_data;
+       
+       if(NULL==cxt->wag_ctl.open_report_data)
+       {
+               WAG_LOG("wag register control path fail \n");
+               return -1;
+       }
+
+       //add misc dev for sensor hal control cmd
+       err = wag_misc_init(wag_context_obj);
+       if(err)
+       {
+               WAG_ERR("unable to register wag misc device!!\n");
+               return -2;
+       }
+       err = sysfs_create_group(&wag_context_obj->mdev.this_device->kobj,
+                       &wag_attribute_group);
+       if (err < 0)
+       {
+               WAG_ERR("unable to create wag attribute file\n");
+               return -3;
+       }
+       kobject_uevent(&wag_context_obj->mdev.this_device->kobj, KOBJ_ADD);
+       return 0;       
+}
+
+static int wag_probe(struct platform_device *pdev) 
+{
+       int err;
+       WAG_LOG("+++++++++++++wag_probe!!\n");
+
+       wag_context_obj = wag_context_alloc_object();
+       if (!wag_context_obj)
+       {
+               err = -ENOMEM;
+               WAG_ERR("unable to allocate devobj!\n");
+               goto exit_alloc_data_failed;
+       }
+       //init real wag driver
+       err = wag_real_driver_init();
+       if(err)
+       {
+               WAG_ERR("wag real driver init fail\n");
+               goto real_driver_init_fail;
+       }
+
+       //init input dev
+       err = wag_input_init(wag_context_obj);
+       if(err)
+       {
+               WAG_ERR("unable to register wag input device!\n");
+               goto exit_alloc_input_dev_failed;
+       }
+
+#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_EARLYSUSPEND)
+       atomic_set(&(wag_context_obj->early_suspend), 0);
+       wag_context_obj->early_drv.level    = EARLY_SUSPEND_LEVEL_STOP_DRAWING - 1,
+       wag_context_obj->early_drv.suspend  = wag_early_suspend,
+       wag_context_obj->early_drv.resume   = wag_late_resume,    
+       register_early_suspend(&wag_context_obj->early_drv);
+#endif //#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_EARLYSUSPEND)
+  
+       WAG_LOG("----wag_probe OK !!\n");
+       return 0;
+
+       if (err)
+       {
+          WAG_ERR("sysfs node creation error \n");
+          wag_input_destroy(wag_context_obj);
+       }
+       real_driver_init_fail:
+       exit_alloc_input_dev_failed:    
+       kfree(wag_context_obj);
+       exit_alloc_data_failed:
+       WAG_LOG("----wag_probe fail !!!\n");
+       return err;
+}
+
+static int wag_remove(struct platform_device *pdev)
+{
+       int err=0;
+       WAG_FUN(f);
+       input_unregister_device(wag_context_obj->idev);        
+       sysfs_remove_group(&wag_context_obj->idev->dev.kobj,
+                               &wag_attribute_group);
+       
+       if((err = misc_deregister(&wag_context_obj->mdev)))
+       {
+               WAG_ERR("misc_deregister fail: %d\n", err);
+       }
+       kfree(wag_context_obj);
+       return 0;
+}
+
+static void wag_early_suspend(struct early_suspend *h) 
+{
+       atomic_set(&(wag_context_obj->early_suspend), 1);
+       if(!atomic_read(&wag_context_obj->wake)) //not wake up, disable in early suspend
+       {
+               wag_real_enable(WAG_SUSPEND);
+       }
+       WAG_LOG(" wag_early_suspend ok------->hwm_obj->early_suspend=%d \n",atomic_read(&(wag_context_obj->early_suspend)));
+       return ;
+}
+/*----------------------------------------------------------------------------*/
+static void wag_late_resume(struct early_suspend *h)
+{
+       atomic_set(&(wag_context_obj->early_suspend), 0);
+       if(!atomic_read(&wag_context_obj->wake) && resume_enable_status) //not wake up, disable in early suspend
+       {
+               wag_real_enable(WAG_RESUME);
+       }
+       WAG_LOG(" wag_late_resume ok------->hwm_obj->early_suspend=%d \n",atomic_read(&(wag_context_obj->early_suspend)));
+       return ;
+}
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(USE_EARLY_SUSPEND)
+static int wag_suspend(struct platform_device *dev, pm_message_t state) 
+{
+       atomic_set(&(wag_context_obj->suspend), 1);
+       if(!atomic_read(&wag_context_obj->wake)) //not wake up, disable in early suspend
+       {
+               wag_real_enable(WAG_SUSPEND);
+       }
+       WAG_LOG(" wag_suspend ok------->hwm_obj->suspend=%d \n",atomic_read(&(wag_context_obj->suspend)));
+       return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int wag_resume(struct platform_device *dev)
+{
+       atomic_set(&(wag_context_obj->suspend), 0);
+       if(!atomic_read(&wag_context_obj->wake) && resume_enable_status) //not wake up, disable in early suspend
+       {
+               wag_real_enable(WAG_RESUME);
+       }
+       WAG_LOG(" wag_resume ok------->hwm_obj->suspend=%d \n",atomic_read(&(wag_context_obj->suspend)));
+       return 0;
+}
+#endif //#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(USE_EARLY_SUSPEND)
+
+#ifdef CONFIG_OF
+static const struct of_device_id m_wag_pl_of_match[] = {
+       { .compatible = "mediatek,m_wag_pl", },
+       {},
+};
+#endif
+
+static struct platform_driver wag_driver =
+{
+       .probe      = wag_probe,
+       .remove     = wag_remove,    
+#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(USE_EARLY_SUSPEND)
+       .suspend    = wag_suspend,
+       .resume     = wag_resume,
+#endif
+       .driver     = 
+       {
+               .name = WAG_PL_DEV_NAME,
+               #ifdef CONFIG_OF
+               .of_match_table = m_wag_pl_of_match,
+               #endif
+       }
+};
+
+static int __init wag_init(void) 
+{
+       WAG_FUN();
+
+       if(platform_driver_register(&wag_driver))
+       {
+               WAG_ERR("failed to register wag driver\n");
+               return -ENODEV;
+       }
+       
+       return 0;
+}
+
+static void __exit wag_exit(void)
+{
+       platform_driver_unregister(&wag_driver); 
+       platform_driver_unregister(&wake_gesture_driver);      
+}
+
+late_initcall(wag_init);
+//module_init(wag_init);
+//module_exit(wag_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("WAG device driver");
+MODULE_AUTHOR("Mediatek");
+