/*  open command for l3g4200d device file  */
static int l3g4200d_open(struct inode *inode, struct file *file)
{
	if (gyro->client == NULL) {
		GYRO_DBG( "I2C driver not install\n");
		return -1;
	}
	device_init();
	hrtimer_start(&gyro->timer, ktime_set(0, NORMAL_TM), HRTIMER_MODE_REL);
	gyro->flags = 1;
	GYRO_DBG("l3g4200d has been opened\n");
	return nonseekable_open(inode, file);
}
/*  read command for l3g4200d device file  */
static ssize_t l3g4200d_read(struct file *file, char __user *buf,
				  size_t count, loff_t *offset)
{
	struct l3g4200d_t data;
	if (gyro->client == NULL)
		return -1;
	l3g4200d_read_gyro_values(&data);
	GYRO_DBG("X axis: %d\n", data.x);
	GYRO_DBG("Y axis: %d\n", data.y);
	GYRO_DBG("Z axis: %d\n", data.z);
	return 0;
}
static void gy_late_resume(struct early_suspend *h)
{
	struct l3g4200d_data *gy;
	gy = container_of(h, struct l3g4200d_data, early_suspend);
	l3g4200d_resume(gy->client);
	GYRO_DBG("L3G4200D driver l3g4200d_resume\n");
}
static void gy_early_suspend(struct early_suspend *h)
{
	struct l3g4200d_data *gy;
	gy = container_of(h, struct l3g4200d_data, early_suspend);
	l3g4200d_suspend(gy->client, PMSG_SUSPEND);
	GYRO_DBG("L3G4200D driver gy_early_suspend\n");
}
/*  release command for l3g4200d device file */
static int l3g4200d_close(struct inode *inode, struct file *file)
{
	int ret = -1;
	if (gyro == NULL || gyro->client == NULL) {
		GYRO_DBG( "I2C driver not install\n");
		return -1;
	}
	hrtimer_cancel(&gyro->timer);
	ret = l3g4200d_set_mode(PM_OFF);
	if(ret < 0)
	{
		printk(KERN_ERR "close device i2c set mode PM_OFF err!\n");	
	}
	gyro->flags = -1;
	GYRO_DBG("L3G4200D has been closed\n");
	return 0;
}
/*  write command for l3g4200d device file */
static ssize_t l3g4200d_write(struct file *file, const char __user *buf,
				   size_t count, loff_t *offset)
{
	if (gyro->client == NULL)
		return -1;
	
	GYRO_DBG("l3g4200d should be accessed with ioctl command\n");
	return 0;
}
static void gy_work_func(struct work_struct *work)
{
	int sesc = 0;
	struct l3g4200d_data *gy = container_of(work, struct l3g4200d_data, work);
	memset(&sensor_data, 0, sizeof(sensor_data));
	if (gy->client == NULL)
	{
		printk(KERN_ERR "gy client is null");
		return;
	}
	sesc = 0;
	l3g4200d_read_gyro_values(&sensor_data);
	GYRO_DBG( "X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
	if (atomic_read(&a_flag)) 
	{
		GYRO_DBG("report fusion event to input dev!");
		input_report_abs(gy->input_dev, ABS_RX, fusiondata[0]);		
		input_report_abs(gy->input_dev, ABS_RY, fusiondata[1]);		
		input_report_abs(gy->input_dev, ABS_RZ, fusiondata[2]);
		input_report_abs(gy->input_dev, ABS_THROTTLE, fusiondata[3]);				
		input_report_abs(gy->input_dev, ABS_RUDDER, fusiondata[4]);		
		input_report_abs(gy->input_dev, ABS_WHEEL, fusiondata[5]);
		input_report_abs(gy->input_dev, ABS_GAS, fusiondata[6]);			
		input_report_abs(gy->input_dev, ABS_BRAKE, fusiondata[7]);		
		input_report_abs(gy->input_dev, ABS_HAT0X, fusiondata[8]);
		input_report_abs(gy->input_dev, ABS_HAT0Y, fusiondata[9]);				
		atomic_set(&a_flag, 0);
	}
	input_report_abs(gy->input_dev, ABS_X, sensor_data.x);//cross x,y adapter hal sensors_akm8973.c			
	input_report_abs(gy->input_dev, ABS_Y, sensor_data.y);		
	input_report_abs(gy->input_dev, ABS_Z, sensor_data.z);
	input_sync(gy->input_dev);
	/*initalize timer os gyro*/
	if(gyro->flags > 0)
	{
		hrtimer_start(&gy->timer, ktime_set(sesc, NORMAL_TM), HRTIMER_MODE_REL);
	}
	else
	{
		hrtimer_start(&gy->timer, ktime_set(SUSPEND_VAL, 0), HRTIMER_MODE_REL);
	}
}
static int l3g4200d_remove(struct i2c_client *client)
{
	struct l3g4200d_data *lis = i2c_get_clientdata(client);
	unregister_early_suspend(&lis->early_suspend);
	hrtimer_cancel(&lis->timer);
	misc_deregister(&gysensor_device);
	input_unregister_device(lis->input_dev);
	kfree(lis);
	GYRO_DBG("L3G4200D driver removing\n");
	return 0;
}
/*  i2c write routine for l3g4200d digital gyroscope */
static char l3g4200d_i2c_write(unsigned char reg_addr,
				    unsigned char *data,
				    unsigned char len)
{
	int dummy;
	int i;

	if (gyro->client == NULL)  /*  No global client pointer? */
		return -1;
	for (i = 0; i < len; i++) {
		dummy = i2c_smbus_write_byte_data(gyro->client,
						  reg_addr++, data[i]);
		if (dummy) {
			GYRO_DBG("i2c write error\n");
			return dummy;
		}
	}
	return 0;
}
/*  i2c read routine for l3g4200d digital gyroscope */
static char l3g4200d_i2c_read(unsigned char reg_addr,
				   unsigned char *data,
				   unsigned char len)
{
	int dummy = 0;
	int i = 0;
	if (gyro->client == NULL)  /*  No global client pointer? */
		return -1;

	while (i < len) {
		dummy = i2c_smbus_read_word_data(gyro->client, reg_addr++);
		if (dummy >= 0) {
			data[i] = dummy & 0x00ff;
			//printk(KERN_ERR" i2c read %d %d %x\n ",i, data[i], reg_addr-1);
			i++;
		} else {
			GYRO_DBG(" i2c read error\n ");
			return dummy;
		}
		dummy = len;
	        //usleep_range(3000, 6000);
	}
	return dummy;
}
static int l3g4200d_probe(struct i2c_client *client,
			       const struct i2c_device_id *devid)
{
	struct l3g4200d_data *data;
	struct gyro_platform_data *platform_data = NULL;
	int ret = -1;
	int tempvalue;
	GYRO_DBG("l3g4200d_probe start!\n");
	
	if (client->dev.platform_data == NULL) {
		dev_err(&client->dev, "platform data is NULL. exiting.\n");
		ret = -ENODEV;
		goto exit;
	}
    platform_data = client->dev.platform_data;
    if(platform_data->gyro_power)
    {
		ret = platform_data->gyro_power(IC_PM_ON);
		if( ret < 0)
	    {
	    	dev_err(&client->dev, "gyro power on error!\n");
	    	goto exit;
	    }
    }  
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		ret = -ENODEV;
		goto exit_pm_off;
	}

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)){
		ret = -ENODEV;
		goto exit_pm_off;
	}
	
	/*
	 * OK. For now, we presume we have a valid client. We now create the
	 * client structure, even though we cannot fill it completely yet.
	 */
	data = kzalloc(sizeof(struct l3g4200d_data), GFP_KERNEL);

	if (NULL == data) {
		dev_err(&client->dev,
			"failed to allocate memory for module data\n");
		ret = -ENOMEM;
		goto exit_pm_off;
	}
	mutex_init(&data->mlock);

	INIT_WORK(&data->work, gy_work_func);
	i2c_set_clientdata(client, data);
	data->client = client;
	data->pdata = platform_data;
	
	ret = l3g4200d_validate_pdata(data);
	if (ret < 0) {
		dev_err(&client->dev, "failed to validate platform data\n");
		goto exit_kfree;
	}

    ret = i2c_smbus_read_byte(client);
	if ( ret < 0) {
		GYRO_DBG("i2c_smbus_read_byte error!!\n");
		goto err_detect_failed;
	} else {
		GYRO_DBG("L3G4200D Device detected!\n");
	}

	/* read chip id */
	tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
	if ((tempvalue & 0x00FF) == 0x00D3) {
		GYRO_DBG("I2C driver registered!\n");
	} else {
		data->client = NULL;
		ret = -ENODEV;
		goto err_detect_failed;
	}
	if (sensor_dev == NULL)
	{
		data->input_dev = input_allocate_device();
		if (data->input_dev == NULL) {
			ret = -ENOMEM;
			printk(KERN_ERR "gs_probe: Failed to allocate input device\n");
			goto err_input_dev_alloc_failed;
		}
       
		data->input_dev->name = "gy_sensors";
		sensor_dev = data->input_dev;

	}else{
		data->input_dev = sensor_dev;
	}
	data->input_dev->id.vendor = VENDOR;
	#if 0
	set_bit(EV_REL,data->input_dev->evbit);
	set_bit(REL_RX, data->input_dev->absbit);
	set_bit(REL_RY, data->input_dev->absbit);
	set_bit(REL_RZ, data->input_dev->absbit);
	#endif
	set_bit(EV_ABS,data->input_dev->evbit);
	/* modify the func of init */
	input_set_abs_params(data->input_dev, ABS_RX, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_RY, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_RZ, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_X, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_Y, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_Z, MIN_VALUE, MAX_VALUE, 0, 0);
	
	input_set_abs_params(data->input_dev, ABS_THROTTLE, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_RUDDER, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_WHEEL, MIN_VALUE, MAX_VALUE, 0, 0);
	
	input_set_abs_params(data->input_dev, ABS_GAS, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_HAT0X, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_HAT0Y, MIN_VALUE, MAX_VALUE, 0, 0);
	input_set_abs_params(data->input_dev, ABS_BRAKE, MIN_VALUE, MAX_VALUE, 0, 0);
	set_bit(EV_SYN,data->input_dev->evbit);
	data->input_dev->id.bustype = BUS_I2C;
	input_set_drvdata(data->input_dev, data);
	ret = input_register_device(data->input_dev);
	if (ret) {
		printk(KERN_ERR "gy_probe: Unable to register %s input device\n", data->input_dev->name);
	/* create l3g-dev device class */
		goto err_input_register_device_failed;
	}
	ret = misc_register(&gysensor_device);

	if (ret) {
		printk(KERN_ERR "gy_probe: gysensor_device register failed\n");
		goto err_misc_device_register_failed;
	}

	hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	data->timer.function = gy_timer_func;
	atomic_set(&a_flag, 0);
	data->flags = -1;
#ifdef CONFIG_HAS_EARLYSUSPEND
	data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	data->early_suspend.suspend = gy_early_suspend;
	data->early_suspend.resume = gy_late_resume;
	register_early_suspend(&data->early_suspend);
#endif
	GYRO_DBG("L3G4200D device created successfully\n");
	gy_wq = create_singlethread_workqueue("gy_wq");
	if (!gy_wq)
		return -ENOMEM;

	gyro = data;
//	hrtimer_start(&this_gs_data->timer, ktime_set(0, 500000000), HRTIMER_MODE_REL);

    #ifdef CONFIG_HUAWEI_HW_DEV_DCT
    /* detect current device successful, set the flag as present */
    set_hw_dev_flag(DEV_I2C_GYROSCOPE);
    #endif
	printk(KERN_DEBUG "l3g4200d_probe   successful");

	return 0;
err_misc_device_register_failed:
	misc_deregister(&gysensor_device);
err_input_register_device_failed:
	input_free_device(gyro->input_dev);
err_input_dev_alloc_failed:
err_detect_failed:
exit_kfree:
	kfree(gyro);
exit_pm_off:
	if(platform_data->gyro_power)
	{
		platform_data->gyro_power(IC_PM_OFF);
	}
exit:
	return ret;
}
/* modify iotcl interface */
static long l3g4200d_ioctl(struct file *file,
			       unsigned int cmd, unsigned long arg)
{
	int err = 0;
	unsigned char data[6];
	short flag = 1;
	int val = 0;
	void __user *argp = (void __user *)arg;
	/* check l3g4200d_client */
	if (gyro->client == NULL) {
		GYRO_DBG( "I2C driver not install\n");
		return -EFAULT;
	}

	/* cmd mapping */

	switch (cmd) {
	case L3G4200D_SET_RANGE:
		if (copy_from_user(data, argp, 1) != 0) {
			GYRO_DBG( "copy_from_user error\n");
			return -EFAULT;
		}
		err = l3g4200d_set_range(*data);
		return err;
		
	case L3G4200D_SET_MODE:
		if (copy_from_user(data, argp, 1) != 0) {
			GYRO_DBG( "copy_to_user error\n");
			return -EFAULT;
		}
		/*enable or disable timer*/
		if( PM_NORMAL == *data )
 		{
			gyro->flags = GYRO_ENABLE;		
			hrtimer_start(&gyro->timer, ktime_set(0, NORMAL_TM), HRTIMER_MODE_REL);
		}	
		else
		{
			err = 0;
			gyro->flags = GYRO_DISABLE;		
		}	
		GYRO_DBG("L3G4200D_SET_MODE %d\n", *data);
		err = l3g4200d_set_mode(*data);
		return err;

	case L3G4200D_SET_BANDWIDTH:
		if (copy_from_user(data, argp, 1) != 0) {
			GYRO_DBG("copy_from_user error\n");
			return -EFAULT;
		}
		err = l3g4200d_set_bandwidth(*data);
		return err;

	case L3G4200D_READ_GYRO_VALUES:
		err = l3g4200d_read_gyro_values(
				(struct l3g4200d_t *)data);

		if (copy_to_user((struct l3g4200d_t *)arg,
				 (struct l3g4200d_t *)data, 6) != 0) {
			GYRO_DBG("copy_to error\n");			
			return -EFAULT;
		}
		return err;
	case L3G4200D_SET_FUSION_DATA:
		if(copy_from_user((void*)fusiondata, argp, sizeof(fusiondata)) != 0){
			return -EFAULT;
		}
		atomic_set(&a_flag, 1);
		GYRO_DBG("L3G4200D_SET_FUSION_DATA copy_from error\n");
		return err;
	case L3G4200D_GET_GYRO_DATA:
		if(copy_to_user(argp, &userdata[0], sizeof(userdata))!= 0)
		{
			return -EFAULT;
		}
		GYRO_DBG("L3G4200D_GET_GYRO_DATA copy_to error\n");
		return 0; 
	case ECS_IOCTL_APP_GET_GYRO_DATA:
		hrtimer_cancel(&gyro->timer);
		memset(&sensor_data, 0, sizeof(sensor_data));
	 	err = l3g4200d_read_gyro_values(&sensor_data);
	 	if(err < 0)
		{
			GYRO_DBG("L3G4200D ECS_IOCTL_APP_GET_GYRO_DATA TEST error!\n");
			return err;
		}
	 	GYRO_DBG(" before X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
		if(copy_to_user(argp, &sensor_data, sizeof(sensor_data))!= 0)
		{
			GYRO_DBG("ECS_IOCTL_APP_GET_GYRO_DATA copy to user error\n");
			return -EFAULT;
		}
		return 0; 
	case ECS_IOCTL_APP_GET_GYRO_CAL:	
		GYRO_DBG("L3G4200D mmi get ret!\n");
		/* self-test flowchart update*/
		hrtimer_cancel(&gyro->timer);
		memset(&sensor_data, 0, sizeof(sensor_data));
	 	err = l3g4200d_read_gyro_values(&sensor_data);
	 	printk(KERN_ERR " before X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
		l3g4200d_set_selftest(L3G4200D_SELFTEST_EN);
		msleep(800);
	 	err = l3g4200d_read_gyro_values(&sensor_data);
	 	printk(KERN_ERR "after X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
		if(err < 0)
		{
			GYRO_DBG("L3G4200D SELF-TEST read error!\n");
			return err;
		}
		/*put data to user*/
		if (copy_to_user(argp, &sensor_data, sizeof(sensor_data))) 
		{
			return -EFAULT;
		}
		l3g4200d_disable_selftest();
		return 0; 		
	case ECS_IOCTL_APP_GET_CAL:	
		GYRO_DBG("L3G4200D mmi get ret!\n");
		/* self-test flowchart update*/
		hrtimer_cancel(&gyro->timer);
		memset(&sensor_data, 0, sizeof(sensor_data));
	 	err = l3g4200d_read_gyro_values(&sensor_data);
	 	printk(KERN_ERR " before X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
		l3g4200d_set_selftest(L3G4200D_SELFTEST_EN);
		msleep(800);
	 	err = l3g4200d_read_gyro_values(&sensor_data);
	 	printk(KERN_ERR "after X axis: %d Y axis: %d Z axis: %d\n", sensor_data.x,sensor_data.y,sensor_data.z);
		if(err < 0)
		{
			GYRO_DBG("L3G4200D SELF-TEST read error!\n");
			return err;
		}
		
		val = abs(sensor_data.x);
		if( (val > MAX_VAL) || (val < MIN_VAL) )
		{
			flag = -1;
			GYRO_DBG("L3G4200D SELF-TEST x %d error!\n", val);	
		}
		
		val = abs(sensor_data.y);
		if( (val > MAX_VAL) || (val < MIN_VAL) )
		{
			flag = -1;
			GYRO_DBG("L3G4200D SELF-TEST y %d error!\n", val);
		}	
		
		val = abs(sensor_data.z);
		if( (val > MAX_VAL) || (val < MIN_VAL) )
		{
			flag = -1;
			GYRO_DBG("L3G4200D SELF-TEST z %d error!\n", val);	
		}
	    if (copy_to_user(argp, &flag, sizeof(flag))) {
			return -EFAULT;
		}
		l3g4200d_disable_selftest();
		return 0; 		
	default:
		return 0;
	}
	return err;
}