//@return, 0:operate successfully
//         > 0: the length of memory size ioctl has accessed,
//         error otherwise.
static long gt1x_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	u32 value = 0;
	s32 ret = 0;		//the initial value must be 0
	u8 *data = NULL;

	GTP_DEBUG("IOCTL CMD:%x", cmd);
	GTP_DEBUG("command:%d, length:%d, rw:%s", _IOC_NR(cmd), _IOC_SIZE(cmd), (_IOC_DIR(cmd) & _IOC_READ) ? "read" : (_IOC_DIR(cmd) & _IOC_WRITE) ? "write" : "-");

	if (_IOC_DIR(cmd)) {
		s32 err = -1;
		s32 data_length = _IOC_SIZE(cmd);
		data = (u8 *) kzalloc(data_length, GFP_KERNEL);
		memset(data, 0, data_length);

		if (_IOC_DIR(cmd) & _IOC_WRITE) {
			err = copy_from_user(data, (void __user *)arg, data_length);
			if (err) {
				GTP_DEBUG("Can't access the memory.");
				kfree(data);
				return -1;
			}
		}
	} else {
		value = (u32) arg;
	}

	switch (cmd & NEGLECT_SIZE_MASK) {
	case IO_GET_VERISON:
		if ((u8 __user *) arg) {
			ret = copy_to_user(((u8 __user *) arg), IO_VERSION, sizeof(IO_VERSION));
			if (!ret) {
				ret = sizeof(IO_VERSION);
			}
			GTP_INFO("%s", IO_VERSION);
		}
		break;
	case IO_IIC_READ:
		ret = io_iic_read(data, (void __user *)arg);
		break;

	case IO_IIC_WRITE:
		ret = io_iic_write(data);
		break;

	case IO_RESET_GUITAR:
		gt1x_reset_guitar();
		break;

	case IO_DISABLE_IRQ:
		gt1x_irq_disable();
#if GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_OFF);
#endif
		break;

	case IO_ENABLE_IRQ:
		gt1x_irq_enable();
#if GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_ON);
#endif
		break;

		//print a string to syc log messages between application and kernel.
	case IO_PRINT:
		if (data)
			GTP_INFO("%s", (char *)data);
		break;

#if GTP_GESTURE_WAKEUP
	case GESTURE_ENABLE_TOTALLY:
		GTP_DEBUG("ENABLE_GESTURE_TOTALLY");
		gesture_enabled = (is_all_dead(gestures_flag, sizeof(gestures_flag)) ? 0 : 1);
		break;

	case GESTURE_DISABLE_TOTALLY:
		GTP_DEBUG("DISABLE_GESTURE_TOTALLY");
		gesture_enabled = 0;
		break;

	case GESTURE_ENABLE_PARTLY:
		SETBIT(gestures_flag, (u8) value);
		gesture_enabled = 1;
		GTP_DEBUG("ENABLE_GESTURE_PARTLY, gesture = 0x%02X, gesture_enabled = %d", value, gesture_enabled);
		break;

	case GESTURE_DISABLE_PARTLY:
		ret = QUERYBIT(gestures_flag, (u8) value);
		if (!ret) {
			break;
		}
		CLEARBIT(gestures_flag, (u8) value);
		if (is_all_dead(gestures_flag, sizeof(gestures_flag))) {
			gesture_enabled = 0;
		}
		GTP_DEBUG("DISABLE_GESTURE_PARTLY, gesture = 0x%02X, gesture_enabled = %d", value, gesture_enabled);
		break;

	case GESTURE_DATA_OBTAIN:
		GTP_DEBUG("OBTAIN_GESTURE_DATA");

		mutex_lock(&gesture_data_mutex);
		if (gesture_data.data[1] > GESTURE_MAX_POINT_COUNT) {
			gesture_data.data[1] = GESTURE_MAX_POINT_COUNT;
		}
		if (gesture_data.data[3] > 80) {
			gesture_data.data[3] = 80;
		}
		ret = copy_to_user(((u8 __user *) arg), &gesture_data.data, 4 + gesture_data.data[1] * 4 + gesture_data.data[3]);
		mutex_unlock(&gesture_data_mutex);
		if (ret) {
			GTP_ERROR("ERROR when copy gesture data to user.");
			ret = ERROR_MEM;
		} else {
			ret = 4 + gesture_data.data[1] * 4 + gesture_data.data[3];
		}
		break;

	case GESTURE_DATA_ERASE:
		GTP_DEBUG("ERASE_GESTURE_DATA");
		gesture_clear_wakeup_data();
		break;
#endif // GTP_GESTURE_WAKEUP

#if GTP_HOTKNOT
	case HOTKNOT_LOAD_HOTKNOT:
		ret = hotknot_load_hotknot_subsystem();
		break;

	case HOTKNOT_LOAD_AUTHENTICATION:
#if GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_ON);
#endif
		ret = hotknot_load_authentication_subsystem();
		break;

	case HOTKNOT_RECOVERY_MAIN:
		ret = hotknot_recovery_main_system();
		break;
#if HOTKNOT_BLOCK_RW
	case HOTKNOT_DEVICES_PAIRED:
		hotknot_paired_flag = 0;
		force_wake_flag = 0;
		block_enable = 1;
		ret = hotknot_block_rw(HN_DEVICE_PAIRED, (s32) value);
		break;

	case HOTKNOT_MASTER_SEND:
		ret = hotknot_block_rw(HN_MASTER_SEND, (s32) value);
		if (!ret)
			ret = got_hotknot_extra_state;
		break;

	case HOTKNOT_SLAVE_RECEIVE:
		ret = hotknot_block_rw(HN_SLAVE_RECEIVED, (s32) value);
		if (!ret)
			ret = got_hotknot_extra_state;
		break;

	case HOTKNOT_MASTER_DEPARTED:
		ret = hotknot_block_rw(HN_MASTER_DEPARTED, (s32) value);
		break;

	case HOTKNOT_SLAVE_DEPARTED:
		ret = hotknot_block_rw(HN_SLAVE_DEPARTED, (s32) value);
		break;

	case HOTKNOT_WAKEUP_BLOCK:
		hotknot_wakeup_block();
		break;
#endif //HOTKNOT_BLOCK_RW
#endif //GTP_HOTKNOT

	default:
		GTP_INFO("Unknown cmd.");
		ret = -1;
		break;
	}

	if (data != NULL) {
		kfree(data);
	}

	return ret;
}
/*@return, 0:operate successfully
/         > 0: the length of memory size ioctl has accessed,
/         error otherwise.*/
static long gt1x_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	u32 value = 0;
	s32 ret = 0;		/*the initial value must be 0*/
	u8 *data = NULL;
	static struct ratelimit_state ratelimit = {
		.lock = __RAW_SPIN_LOCK_UNLOCKED(ratelimit.lock),
		.interval = HZ/2,
		.burst = 1,
		.begin = 1,
	};

	GTP_DEBUG("IOCTL CMD:%x", cmd);
	/*GTP_DEBUG("command:%d, length:%d, rw:%s", _IOC_NR(cmd), _IOC_SIZE(cmd),
		(_IOC_DIR(cmd) & _IOC_READ) ? "read" : (_IOC_DIR(cmd) & _IOC_WRITE) ? "write" : "-");*/

	if (_IOC_DIR(cmd)) {
		s32 err = -1;
		s32 data_length = _IOC_SIZE(cmd);

		data = kzalloc(data_length, GFP_KERNEL);
		memset(data, 0, data_length);

		if (_IOC_DIR(cmd) & _IOC_WRITE) {
			err = copy_from_user(data, (void __user *)arg, data_length);
			if (err) {
				GTP_DEBUG("Can't access the memory.");
				kfree(data);
				return -1;
			}
		}
	} else {
		value = (u32) arg;
	}

	switch (cmd & NEGLECT_SIZE_MASK) {
	case IO_GET_VERSION:
		if ((u8 __user *) arg) {
			ret = copy_to_user(((u8 __user *) arg), IO_VERSION, sizeof(IO_VERSION));
			if (!ret)
				ret = sizeof(IO_VERSION);
			GTP_INFO("%s", IO_VERSION);
		}
		break;
	case IO_IIC_READ:
		if (1 == gt1x_is_tpd_halt()) {
			if (__ratelimit(&ratelimit))
				GTP_ERROR("touch is suspended.");
			break;
		}
		ret = io_iic_read(data, (void __user *)arg);
		break;

	case IO_IIC_WRITE:
		if (1 == gt1x_is_tpd_halt()) {
			if (__ratelimit(&ratelimit))
				GTP_ERROR("touch is suspended.");
			break;
		}
		ret = io_iic_write(data);
		break;

	case IO_RESET_GUITAR:
		gt1x_reset_guitar();
		break;

	case IO_DISABLE_IRQ:
		gt1x_irq_disable();
#ifdef CONFIG_GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_OFF);
#endif
		break;

	case IO_ENABLE_IRQ:
		gt1x_irq_enable();
#ifdef CONFIG_GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_ON);
#endif
		break;

		/*print a string to syc log messages between application and kernel.*/
	case IO_PRINT:
		if (data)
			GTP_INFO("%s", (char *)data);
		break;

#ifdef CONFIG_GTP_GESTURE_WAKEUP
	case GESTURE_ENABLE_TOTALLY:
		GTP_DEBUG("ENABLE_GESTURE_TOTALLY");
		gesture_enabled = (is_all_dead(gestures_flag, sizeof(gestures_flag)) ? 0 : 1);
		break;

	case GESTURE_DISABLE_TOTALLY:
		GTP_DEBUG("DISABLE_GESTURE_TOTALLY");
		gesture_enabled = 0;
		break;

	case GESTURE_ENABLE_PARTLY:
		SETBIT(gestures_flag, (u8) value);
		gesture_enabled = 1;
		GTP_DEBUG("ENABLE_GESTURE_PARTLY, gesture = 0x%02X, gesture_enabled = %d", value, gesture_enabled);
		break;

	case GESTURE_DISABLE_PARTLY:
		ret = QUERYBIT(gestures_flag, (u8) value);
		if (!ret)
			break;
		CLEARBIT(gestures_flag, (u8) value);
		if (is_all_dead(gestures_flag, sizeof(gestures_flag)))
			gesture_enabled = 0;
		GTP_DEBUG("DISABLE_GESTURE_PARTLY, gesture = 0x%02X, gesture_enabled = %d", value, gesture_enabled);
		break;

	case GESTURE_DATA_OBTAIN:
		GTP_DEBUG("OBTAIN_GESTURE_DATA");

		mutex_lock(&gesture_data_mutex);
		if (gesture_data.data[1] > GESTURE_MAX_POINT_COUNT)
			gesture_data.data[1] = GESTURE_MAX_POINT_COUNT;
		if (gesture_data.data[3] > 80)
			gesture_data.data[3] = 80;
		ret =
		    copy_to_user(((u8 __user *) arg), &gesture_data.data,
				 4 + gesture_data.data[1] * 4 + gesture_data.data[3]);
		mutex_unlock(&gesture_data_mutex);
		if (ret) {
			GTP_ERROR("ERROR when copy gesture data to user.");
			ret = ERROR_MEM;
		} else {
			ret = 4 + gesture_data.data[1] * 4 + gesture_data.data[3];
		}
		break;

	case GESTURE_DATA_ERASE:
		GTP_DEBUG("ERASE_GESTURE_DATA");
		gesture_clear_wakeup_data();
		break;
#endif				/*CONFIG_GTP_GESTURE_WAKEUP*/

#ifdef CONFIG_GTP_HOTKNOT
	case HOTKNOT_LOAD_HOTKNOT:
		ret = hotknot_load_hotknot_subsystem();
		break;

	case HOTKNOT_LOAD_AUTHENTICATION:
		if (1 == gt1x_is_tpd_halt()) {
			GTP_ERROR("touch is suspended.");
			break;
		}
#ifdef CONFIG_GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_ON);
#endif
		ret = hotknot_load_authentication_subsystem();
		break;

	case HOTKNOT_RECOVERY_MAIN:
		if (1 == gt1x_is_tpd_halt()) {
			GTP_ERROR("touch is suspended.");
			break;
		}
		ret = hotknot_recovery_main_system();
		break;
#ifdef CONFIG_HOTKNOT_BLOCK_RW
	case HOTKNOT_DEVICES_PAIRED:
		hotknot_paired_flag = 0;
		force_wake_flag = 0;
		block_enable = 1;
		ret = hotknot_block_rw(HN_DEVICE_PAIRED, (s32) value);
		break;

	case HOTKNOT_MASTER_SEND:
		ret = hotknot_block_rw(HN_MASTER_SEND, (s32) value);
		if (!ret)
			ret = got_hotknot_extra_state;
		break;

	case HOTKNOT_SLAVE_RECEIVE:
		ret = hotknot_block_rw(HN_SLAVE_RECEIVED, (s32) value);
		if (!ret)
			ret = got_hotknot_extra_state;
		break;

	case HOTKNOT_MASTER_DEPARTED:
		ret = hotknot_block_rw(HN_MASTER_DEPARTED, (s32) value);
		break;

	case HOTKNOT_SLAVE_DEPARTED:
		ret = hotknot_block_rw(HN_SLAVE_DEPARTED, (s32) value);
		break;

	case HOTKNOT_WAKEUP_BLOCK:
		hotknot_wakeup_block();
		break;
#endif				/*CONFIG_HOTKNOT_BLOCK_RW*/
#endif				/*CONFIG_GTP_HOTKNOT*/

	default:
		GTP_INFO("Unknown cmd.");
		ret = -1;
		break;
	}

	if (data != NULL)
		kfree(data);
	return ret;
}
#ifdef CONFIG_GTP_HOTKNOT
#ifdef CONFIG_COMPAT
static long gt1x_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	long ret;
	void __user *arg32 = NULL;

	GTP_DEBUG("gt1x_compat_ioctl cmd = %x, arg: 0x%lx\n", cmd, arg);
	arg32 = compat_ptr(arg);
	if (!file->f_op || !file->f_op->unlocked_ioctl)
		return -ENOTTY;

	/*GTP_DEBUG("gt1x_compat_ioctl arg: 0x%lx, arg32: 0x%p\n",arg, arg32);*/

	switch (cmd & NEGLECT_SIZE_MASK) {
	case COMPAT_IO_GET_VERSION:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_GET_VERSION\n");*/
		if (arg32 == NULL) {
			GTP_ERROR("invalid argument.");
			return -EINVAL;
		}

		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_IIC_READ:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_IIC_READ\n");*/
		if (arg32 == NULL) {
			GTP_ERROR("invalid argument.");
			return -EINVAL;
		}

		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_IIC_WRITE:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_IIC_WRITE\n");*/
		if (arg32 == NULL) {
			GTP_ERROR("invalid argument.");
			return -EINVAL;
		}
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_RESET_GUITAR:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_RESET_GUITAR\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_DISABLE_IRQ:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_DISABLE_IRQ\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_ENABLE_IRQ:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_ENABLE_IRQ\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_IO_PRINT:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_IO_PRINT\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_ENABLE_TOTALLY:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_ENABLE_TOTALLY\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_DISABLE_TOTALLY:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_DISABLE_TOTALLY\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_ENABLE_PARTLY:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_ENABLE_PARTLY\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_DISABLE_PARTLY:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_DISABLE_PARTLY\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_DATA_OBTAIN:
		if (arg32 == NULL) {
			GTP_ERROR("invalid argument.");
			return -EINVAL;
		}
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_DATA_OBTAIN\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_GESTURE_DATA_ERASE:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_GESTURE_DATA_ERASE\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_LOAD_HOTKNOT:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_LOAD_HOTKNOT\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_LOAD_AUTHENTICATION:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_LOAD_AUTHENTICATION\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_RECOVERY_MAIN:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_RECOVERY_MAIN\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_DEVICES_PAIRED:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_DEVICES_PAIRED\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_MASTER_SEND:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_MASTER_SEND\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_SLAVE_RECEIVE:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_SLAVE_RECEIVE\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_MASTER_DEPARTED:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_MASTER_DEPARTED\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_SLAVE_DEPARTED:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_SLAVE_DEPARTED\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	case COMPAT_HOTKNOT_WAKEUP_BLOCK:
		/*GTP_DEBUG("gt1x_compat_ioctl COMPAT_HOTKNOT_WAKEUP_BLOCK\n");*/
		ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
		break;
	default:
		GTP_INFO("Unknown cmd.");
		ret = -1;
		break;
	}
	return ret;
}