/** 
 * fxevdev.c
 *
 * Copyright (C) 2015 Fuji Xerox Co.,Ltd. All rights reserved.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called COPYING.
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/page.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#define MINOR_COUNT 1
#define MAX_MULTITOUCH_NUM 4
static struct input_dev *inputdev;
static struct input_dev *inputdev_key;
static dev_t dev_id;
static struct cdev c_dev;
static struct class *class;
static struct semaphore cdev_sem;
static int fxevdev_max_x = 800;
static int fxevdev_max_y = 480;
static int enable_home_long_press = 0;
module_param(fxevdev_max_x, int, S_IRUGO | S_IWUSR);
module_param(fxevdev_max_y, int, S_IRUGO | S_IWUSR);
module_param(enable_home_long_press, int, S_IRUGO | S_IWUSR);
static int fxevdev_open( struct inode *inode, struct file *filp )
{
 return 0;
}
static int fxevdev_close( struct inode* inode, struct file* filp )
{
 return 0;
}
#if 0
static ssize_t fxevdev_read( struct file* filp, char* buf, size_t count, loff_t* pos )
{
 return count;
}
#endif
typedef struct touch_event {
 int id;
        int n;
        int x;
        int y;
        int action;
} touch_event_t;
typedef struct press_event {
 int id;
        int keyid;
} press_event_t;
typedef struct release_event {
 int id;
        int keyid;
} release_event_t;
static touch_event_t tev;
static press_event_t pev;
static release_event_t rev;
static touch_event_t tev_col[MAX_MULTITOUCH_NUM];
static unsigned long push_start;
static ssize_t fxevdev_write( struct file* filp, const char __user *buf, size_t count, loff_t* pos )
{
 int i;
 unsigned char event;
 unsigned long rlen;
 rlen = copy_from_user(&event, (unsigned char*)buf, 1);
 switch (event){
 case 0x01:
  rlen = copy_from_user(&tev, (unsigned char*)buf,
      sizeof(touch_event_t));
  tev_col[tev.n - 1].x = tev.x;
  tev_col[tev.n - 1].y = tev.y;
  tev_col[tev.n - 1].action = tev.action;
  for (i = 0; i < MAX_MULTITOUCH_NUM; i++){
   input_mt_slot(inputdev, i);
   if (i == tev.n - 1){
    input_mt_report_slot_state(inputdev, MT_TOOL_FINGER, tev.action);
   }
   if (tev_col[i].action == 0x01){
    input_report_abs(inputdev, ABS_MT_POSITION_X, tev_col[i].x);
    input_report_abs(inputdev, ABS_MT_POSITION_Y, tev_col[i].y);
   }
   input_mt_sync(inputdev);
  }
  input_sync(inputdev);
  break;
 case 0x03:
  rlen = copy_from_user(&pev, (unsigned char*)buf,
      sizeof(press_event_t));
  push_start = jiffies;
  if (enable_home_long_press == 0){
   input_report_key(inputdev_key, pev.keyid, 1);
   input_sync(inputdev_key);
  }
  break;
 case 0x04:
  rlen = copy_from_user(&rev, (unsigned char*)buf,
      sizeof(release_event_t));
  if (enable_home_long_press == 0){
   input_report_key(inputdev_key, rev.keyid, 0);
  } else if (time_before(jiffies, push_start + 5*HZ)){
   input_report_key(inputdev_key, KEY_LEFTCTRL, 1);
   input_report_key(inputdev_key, KEY_8, 1);
   input_sync(inputdev_key);
   input_report_key(inputdev_key, KEY_8, 0);
   input_report_key(inputdev_key, KEY_LEFTCTRL, 0);
  } else {
   input_report_key(inputdev_key, KEY_LEFTCTRL, 1);
   input_report_key(inputdev_key, KEY_0, 1);
   input_sync(inputdev_key);
   input_report_key(inputdev_key, KEY_0, 0);
   input_report_key(inputdev_key, KEY_LEFTCTRL, 0);
  }
  input_sync(inputdev_key);
  break;
 default:
  return -1;
 }
 return count - rlen;
}
static struct file_operations fxevdev_fops ={
 .owner = THIS_MODULE,
 .open = fxevdev_open,
 .release = fxevdev_close,
 .write = fxevdev_write,
};
static int fxevdev_init(void)
{
 int i;
 int ret;
 struct device * device;
 ret = alloc_chrdev_region( &dev_id, 0, MINOR_COUNT, "fxevdev");
 if( ret < 0 ){
  printk( KERN_ERR "alloc_chrdev_region failed\n" );
  return ret;
 }
 cdev_init( &c_dev, &fxevdev_fops );
 c_dev.owner = THIS_MODULE;
 ret = cdev_add( &c_dev, dev_id, MINOR_COUNT );
 if( ret < 0 ){
  printk( KERN_ERR "cdev_add failed\n" );
  return ret;
 }
  class = class_create(THIS_MODULE, "fxevdev");
  if (IS_ERR(class)) {
   printk("fxevdev: failed to create class");
   cdev_del(&c_dev);
   unregister_chrdev_region(dev_id, 1);
   return PTR_ERR(class);
  }
 device = device_create(class, NULL, MKDEV(MAJOR(dev_id), 0), NULL, "panelevent%d", 0);
 sema_init( &cdev_sem, 1 );
 inputdev = input_allocate_device();
 inputdev->name = "Fuji Xerox multitouch device";
 inputdev->id.bustype = BUS_VIRTUAL;
 inputdev->id.vendor = 0x0;
 inputdev->id.product = 0x0;
 inputdev->id.version = 0x0;
 inputdev->phys = "fxevdev/input0";
 inputdev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_SYN);
 input_set_abs_params(inputdev, ABS_X,
     0, fxevdev_max_x, 0, 0);
 input_set_abs_params(inputdev, ABS_Y,
     0, fxevdev_max_y, 0, 0);
 input_set_abs_params(inputdev, ABS_MT_POSITION_X,
     0, fxevdev_max_x, 0, 0);
 input_set_abs_params(inputdev, ABS_MT_POSITION_Y,
     0, fxevdev_max_y, 0, 0);
 input_set_abs_params(inputdev, ABS_MT_TRACKING_ID, 0, MAX_MULTITOUCH_NUM, 0, 0);
 input_mt_init_slots(inputdev, MAX_MULTITOUCH_NUM, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
        inputdev_key = input_allocate_device();
        inputdev_key->name = "Fuji Xerox keyevent device";
        inputdev_key->id.bustype = BUS_VIRTUAL;
        inputdev_key->id.vendor = 0x0;
        inputdev_key->id.product = 0x0;
        inputdev_key->id.version = 0x0;
        inputdev_key->phys = "fxevdev/input1";
 inputdev_key->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SYN);
 for (i = 0; i < 249; i++){
  set_bit(i, inputdev_key->keybit);
 }
 ret = input_register_device(inputdev);
 if (ret != 0){
  class_destroy(class);
                cdev_del(&c_dev);
                unregister_chrdev_region(dev_id, MINOR_COUNT);
  return -ENOMEM;
 }
        ret = input_register_device(inputdev_key);
        if (ret != 0){
  input_unregister_device(inputdev);
                class_destroy(class);
                cdev_del(&c_dev);
                unregister_chrdev_region(dev_id, MINOR_COUNT);
                return -ENOMEM;
        }
 for (i = 0; i < MAX_MULTITOUCH_NUM; i++){
  tev_col[i].x = 0;
  tev_col[i].y = 0;
 }
 return 0;
}
static void fxevdev_exit(void)
{
 device_destroy(class, MKDEV(MAJOR(dev_id), 0));
 class_destroy(class);
 cdev_del( &c_dev );
 unregister_chrdev_region( dev_id, MINOR_COUNT );
 input_unregister_device(inputdev_key);
 input_unregister_device(inputdev);
 return;
}
module_init(fxevdev_init);
module_exit(fxevdev_exit);
MODULE_DESCRIPTION("fxevdev");
MODULE_LICENSE("GPL");
