Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
/* drivers/input/touchscreen/gt1x_extents.c
 *
 * 2010 - 2014 Goodix Technology.
 *
 * 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
 * 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 a reference
 * to you, when you are integrating the GOODiX's CTP IC into your system,
 * 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.
 *
 * Version: 1.4
 * Release Date:  2015/07/10
 */

#include <linux/compat.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/input.h>

#include <linux/uaccess.h>
#include <linux/proc_fs.h>	/*proc */

#include <asm/ioctl.h>
#include "gt1x_generic.h"

#if GTP_GESTURE_WAKEUP

#define GESTURE_NODE "goodix_gesture"
#define GESTURE_MAX_POINT_COUNT    64

#pragma pack(1)
typedef struct {
	u8 ic_msg[6];		/*from the first byte */
	u8 gestures[4];
	u8 data[3 + GESTURE_MAX_POINT_COUNT * 4 + 80];	/*80 bytes for extra data */
} st_gesture_data;
#pragma pack()

#define SETBIT(longlong, bit)   (longlong[bit/8] |=  (1 << bit%8))
#define CLEARBIT(longlong, bit) (longlong[bit/8] &= (~(1 << bit%8)))
#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8)))

#define CHKBITS_32          32
#define CHKBITS_16          16
#define CHKBITS_8           8

int gesture_enabled;    /* module switch */
DOZE_T gesture_doze_status = DOZE_DISABLED; /* doze status */

static u8 gestures_flag[32]; /* gesture flag, every bit stands for a gesture */
static st_gesture_data gesture_data; /* gesture data buffer */
static struct mutex gesture_data_mutex; /* lock for gesture data */

static ssize_t gt1x_gesture_data_read(struct file *file, char __user *page, size_t size, loff_t *ppos)
{
	s32 ret = -1;
	GTP_DEBUG("visit gt1x_gesture_data_read. ppos:%d", (int)*ppos);
	if (*ppos) {
		return 0;
	}
	if (size == 4) {
		ret = copy_to_user(((u8 __user *) page), "GT1X", 4);
		return 4;
	}
	ret = simple_read_from_buffer(page, size, ppos, &gesture_data, sizeof(gesture_data));

	GTP_DEBUG("Got the gesture data.");
	return ret;
}

static ssize_t gt1x_gesture_data_write(struct file *filp, const char __user *buff, size_t len, loff_t *off)
{
	s32 ret = 0;

	GTP_DEBUG_FUNC();

	ret = copy_from_user(&gesture_enabled, buff, 1);
	if (ret) {
		GTP_ERROR("copy_from_user failed.");
		return -EPERM;
	}

	GTP_DEBUG("gesture enabled:%x, ret:%d", gesture_enabled, ret);

	return len;
}

/**
 * calc_checksum - Calc checksum.
 * @buf: data to be calc
 * @len: length of buf.
 * @bits: checkbits
 * Return true-pass, false:not pass.
 */
static bool calc_checksum(u8 *buf, int len, int bits)
{
	int i;

	if (bits == CHKBITS_16) {
		u16 chksum, *b = (u16 *)buf;

		if (len % 2) {
			return false;
		}

		len /= 2;
		for (i = 0, chksum = 0; i < len; i++) {
			if (i == len - 1)
				chksum += le16_to_cpu(b[i]);
			else
				chksum += be16_to_cpu(b[i]);
		}
		return chksum == 0 ? true : false;
	} else if (bits == CHKBITS_8) {
		u8 chksum;

		for (i = 0, chksum = 0; i < len; i++) {
			chksum += buf[i];
		}
		return chksum == 0 ? true : false;
	}
	return false;
}

int gesture_enter_doze(void)
{
	int retry = 0;

	GTP_DEBUG_FUNC();
	GTP_DEBUG("Entering doze mode...");
	while (retry++ < 5) {
		if (!gt1x_send_cmd(0x08, 0)) {
			gesture_doze_status = DOZE_ENABLED;
			GTP_DEBUG("Working in doze mode!");
			return 0;
		}
		usleep_range(10000, 11000);
	}
	GTP_ERROR("Send doze cmd failed.");
	return -1;
}

s32 gesture_event_handler(struct input_dev *dev)
{
	u8 doze_buf[4] = { 0 }, ges_type;
	static int err_flag1, err_flag2;
	int len, extra_len, need_chk;
	unsigned int key_code;
	s32 ret = 0;

	if (DOZE_ENABLED != gesture_doze_status) {
		return -1;
	}

	/** package: -head 4B + track points + extra info-
	 * - head -
	 *  doze_buf[0]: gesture type,
	 *  doze_buf[1]: number of gesture points ,
	 *  doze_buf[2]: protocol type,
	 *  doze_buf[3]: gesture extra data length.
	 */
	ret = gt1x_i2c_read(GTP_REG_WAKEUP_GESTURE, doze_buf, 4);
	if (ret < 0) {
		return 0;
	}

	ges_type = doze_buf[0];
	len = doze_buf[1];
	need_chk = doze_buf[2] & 0x80;
	extra_len = doze_buf[3];

	GTP_DEBUG("0x%x = 0x%02X,0x%02X,0x%02X,0x%02X", GTP_REG_WAKEUP_GESTURE,
			doze_buf[0], doze_buf[1], doze_buf[2], doze_buf[3]);

	if (len > GESTURE_MAX_POINT_COUNT) {
		GTP_ERROR("Gesture contain too many points!(%d)", len);
		len = GESTURE_MAX_POINT_COUNT;
	}

	if (extra_len > 32) {
		GTP_ERROR("Gesture contain too many extra data!(%d)", extra_len);
		extra_len = 32;
	}

	/* get gesture extra info */
	if (extra_len >= 0) {
		u8 ges_data[extra_len + 1];

		/* head 4 + extra data * 4 + chksum 1 */
		ret = gt1x_i2c_read(GTP_REG_WAKEUP_GESTURE + 4,
				ges_data, extra_len + 1);
		if (ret < 0) {
			GTP_ERROR("Read extra gesture data failed.");
			return 0;
		}

		if (likely(need_chk)) { /* calc checksum */
			bool val;

			ges_data[extra_len] += doze_buf[0] + doze_buf[1]
				+ doze_buf[2] + doze_buf[3];

			val = calc_checksum(ges_data, extra_len + 1, CHKBITS_8);
			if (unlikely(!val)) { /* check failed */
				GTP_ERROR("Gesture checksum error.");
				if (err_flag1) {
					err_flag1 = 0;
					ret = 0;
					goto clear_reg;
				} else {
					/* just return 0 without clear reg,
					   this will receive another int, we
					   check the data in the next frame */
					err_flag1 = 1;
					return 0;
				}
			}

			err_flag1 = 0;
		}

		mutex_lock(&gesture_data_mutex);
		memcpy(&gesture_data.data[4 + len * 4], ges_data, extra_len);
		mutex_unlock(&gesture_data_mutex);
	}

	/* check gesture type (if available?) */
	if (ges_type == 0 || !QUERYBIT(gestures_flag, ges_type)) {
		GTP_INFO("Gesture[0x%02X] has been disabled.", doze_buf[0]);
		doze_buf[0] = 0x00;
		gt1x_i2c_write(GTP_REG_WAKEUP_GESTURE, doze_buf, 1);
		gesture_enter_doze();
		return 0;
	}

	/* get gesture point data */
	if (len > 0) { /* coor num * 4 + chksum 2*/
		u8 ges_data[len * 4 + 2];

		ret = gt1x_i2c_read(GES_BUFFER_ADDR, ges_data, len * 4);
		if (ret < 0) {
			GTP_ERROR("Read gesture data failed.");
			return 0;
		}

		/* checksum reg for gesture point data */
		ret = gt1x_i2c_read(0x819F, &ges_data[len * 4], 2);
		if (ret < 0) {
			GTP_ERROR("Read gesture data failed.");
			return 0;
		}

		if (likely(need_chk)) {
			bool val = calc_checksum(ges_data,
					len * 4 + 2, CHKBITS_16);
			if (unlikely(!val)) { /* check failed */
				GTP_ERROR("Gesture checksum error.");
				if (err_flag2) {
					err_flag2 = 0;
					ret = 0;
					goto clear_reg;
				} else {
					err_flag2 = 1;
					return 0;
				}
			}

			err_flag2 = 0;
		}

		mutex_lock(&gesture_data_mutex);
		memcpy(&gesture_data.data[4], ges_data, len * 4);
		mutex_unlock(&gesture_data_mutex);
	}

	mutex_lock(&gesture_data_mutex);
	gesture_data.data[0] = ges_type;	/*gesture type*/
	gesture_data.data[1] = len;	        /*gesture points number*/
	gesture_data.data[2] = doze_buf[2] & 0x7F; /*protocol type*/
	gesture_data.data[3] = extra_len;   /*gesture date length*/
	mutex_unlock(&gesture_data_mutex);

	/* get key code */
	key_code = ges_type < 16 ? KEY_GES_CUSTOM : KEY_GES_REGULAR;
	GTP_DEBUG("Gesture: 0x%02X, points: %d", doze_buf[0], doze_buf[1]);

	input_report_key(dev, key_code, 1);
	input_sync(dev);
	input_report_key(dev, key_code, 0);
	input_sync(dev);

clear_reg:
	doze_buf[0] = 0; /*clear ges flag*/
	gt1x_i2c_write(GTP_REG_WAKEUP_GESTURE, doze_buf, 1);
	return ret;
}

void gesture_clear_wakeup_data(void)
{
	mutex_lock(&gesture_data_mutex);
	memset(gesture_data.data, 0, 4);
	mutex_unlock(&gesture_data_mutex);
}

void gt1x_gesture_debug(int on)
{
	if (on) {
		gesture_enabled = 1;
		memset(gestures_flag, 0xFF, sizeof(gestures_flag));
	} else {
		gesture_enabled = 0;
		memset(gestures_flag, 0x00, sizeof(gestures_flag));
		gesture_doze_status = DOZE_DISABLED;
	}
	GTP_DEBUG("Gesture debug %s", on ? "on":"off");
}

#endif /* GTP_GESTURE_WAKEUP */

/*HotKnot module*/
#if GTP_HOTKNOT
#define HOTKNOT_NODE "hotknot"
#define HOTKNOT_VERSION  "GOODIX,GT1X"
u8 hotknot_enabled;
u8 hotknot_transfer_mode;

static int hotknot_open(struct inode *node, struct file *flip)
{
	GTP_DEBUG("Hotknot is enabled.");
	hotknot_enabled = 1;
	return 0;
}

static int hotknot_release(struct inode *node, struct file *filp)
{
	GTP_DEBUG("Hotknot is disabled.");
	hotknot_enabled = 0;
	return 0;
}

static s32 hotknot_enter_transfer_mode(void)
{
	int ret = 0;
	u8 buffer[5] = { 0 };

	hotknot_transfer_mode = 1;
#if GTP_ESD_PROTECT
	gt1x_esd_switch(SWITCH_OFF);
#endif

	gt1x_irq_disable();
	gt1x_send_cmd(GTP_CMD_HN_TRANSFER, 0);
	msleep(100);
	gt1x_irq_enable();

	ret = gt1x_i2c_read(0x8140, buffer, sizeof(buffer));
	if (ret) {
		hotknot_transfer_mode = 0;
		return ret;
	}

	buffer[4] = 0;
	GTP_DEBUG("enter transfer mode: %s ", buffer);
	if (strcmp(buffer, "GHot")) {
		hotknot_transfer_mode = 0;
		return ERROR_HN_VER;
	}

	return 0;
}

static s32 hotknot_load_hotknot_subsystem(void)
{
	return hotknot_enter_transfer_mode();
}

static s32 hotknot_load_authentication_subsystem(void)
{
	s32 ret = 0;
	u8 buffer[5] = { 0 };
	ret = gt1x_hold_ss51_dsp_no_reset();
	if (ret < 0) {
		GTP_ERROR("Hold ss51 fail!");
		return ERROR;
	}

	if (gt1x_chip_type == CHIP_TYPE_GT1X) {
		GTP_INFO("hotknot load jump code.");
		ret = gt1x_load_patch(gt1x_patch_jump_fw, 4096, 0, 1024 * 8);
		if (ret < 0) {
			GTP_ERROR("Load jump code fail!");
			return ret;
		}
		GTP_INFO("hotknot load auth code.");
		ret = gt1x_load_patch(hotknot_auth_fw, 4096, 4096, 1024 * 8);
		if (ret < 0) {
			GTP_ERROR("Load auth system fail!");
			return ret;
		}
	} else { /* GT2X */
		GTP_INFO("hotknot load auth code.");
		ret = gt1x_load_patch(hotknot_auth_fw, 4096, 0, 1024 * 6);
		if (ret < 0) {
			GTP_ERROR("load auth system fail!");
			return ret;
		}
	}

	ret = gt1x_startup_patch();
	if (ret < 0) {
		GTP_ERROR("Startup auth system fail!");
		return ret;
	}
	ret = gt1x_i2c_read(GTP_REG_VERSION, buffer, 4);
	if (ret < 0) {
		GTP_ERROR("i2c read error!");
		return ERROR_IIC;
	}
	buffer[4] = 0;
	GTP_INFO("Current System version: %s", buffer);
	return 0;
}

static s32 hotknot_recovery_main_system(void)
{
	gt1x_irq_disable();
	gt1x_reset_guitar();
	gt1x_irq_enable();
#if GTP_ESD_PROTECT
	gt1x_esd_switch(SWITCH_ON);
#endif
	hotknot_transfer_mode = 0;
	return 0;
}

#if HOTKNOT_BLOCK_RW
DECLARE_WAIT_QUEUE_HEAD(bp_waiter);
static u8 got_hotknot_state;
static u8 got_hotknot_extra_state;
static u8 wait_hotknot_state;
static u8 force_wake_flag;
static u8 block_enable;
s32 hotknot_paired_flag;

static s32 hotknot_block_rw(u8 rqst_hotknot_state, s32 wait_hotknot_timeout)
{
	s32 ret = 0;

	wait_hotknot_state |= rqst_hotknot_state;
	GTP_DEBUG("Goodix tool received wait polling state:0x%x,timeout:%d, all wait state:0x%x", rqst_hotknot_state, wait_hotknot_timeout, wait_hotknot_state);
	got_hotknot_state &= (~rqst_hotknot_state);

	set_current_state(TASK_INTERRUPTIBLE);
	if (wait_hotknot_timeout <= 0) {
		wait_event_interruptible(bp_waiter, force_wake_flag || rqst_hotknot_state == (got_hotknot_state & rqst_hotknot_state));
	} else {
		wait_event_interruptible_timeout(bp_waiter, force_wake_flag || rqst_hotknot_state == (got_hotknot_state & rqst_hotknot_state), wait_hotknot_timeout);
	}

	wait_hotknot_state &= (~rqst_hotknot_state);

	if (rqst_hotknot_state != (got_hotknot_state & rqst_hotknot_state)) {
		GTP_ERROR("Wait 0x%x block polling waiter failed.", rqst_hotknot_state);
		ret = -1;
	}

	force_wake_flag = 0;
	return ret;
}

static void hotknot_wakeup_block(void)
{
	GTP_DEBUG("Manual wakeup all block polling waiter!");
	got_hotknot_state = 0;
	wait_hotknot_state = 0;
	force_wake_flag = 1;
	wake_up_interruptible(&bp_waiter);
}

s32 hotknot_event_handler(u8 *data)
{
	u8 hn_pxy_state = 0;
	u8 hn_pxy_state_bak = 0;
	static u8 hn_paired_cnt;
	u8 hn_state_buf[10] = { 0 };
	u8 finger = data[0];
	u8 id = 0;

	if (block_enable && !hotknot_paired_flag && (finger & 0x0F)) {
		id = data[1];
		hn_pxy_state = data[2] & 0x80;
		hn_pxy_state_bak = data[3] & 0x80;
		if ((32 == id) && (0x80 == hn_pxy_state) && (0x80 == hn_pxy_state_bak)) {
#ifdef HN_DBLCFM_PAIRED
			if (hn_paired_cnt++ < 2) {
				return 0;
			}
#endif
			GTP_DEBUG("HotKnot paired!");
			if (wait_hotknot_state & HN_DEVICE_PAIRED) {
				GTP_DEBUG("INT wakeup HN_DEVICE_PAIRED block polling waiter");
				got_hotknot_state |= HN_DEVICE_PAIRED;
				wake_up_interruptible(&bp_waiter);
			}
			block_enable = 0;
			hotknot_paired_flag = 1;
			return 0;
		} else {
			got_hotknot_state &= (~HN_DEVICE_PAIRED);
			hn_paired_cnt = 0;
		}
	}

	if (hotknot_paired_flag) {
		s32 ret = -1;
		ret = gt1x_i2c_read(GTP_REG_HN_STATE, hn_state_buf, 6);
		if (ret < 0) {
			GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
			return 0;
		}

		got_hotknot_state = 0;

		GTP_DEBUG("wait_hotknot_state:%x", wait_hotknot_state);
		GTP_DEBUG("[0x8800~0x8803]=0x%x,0x%x,0x%x,0x%x", hn_state_buf[0], hn_state_buf[1], hn_state_buf[2], hn_state_buf[3]);

		if (wait_hotknot_state & HN_MASTER_SEND) {
			if ((0x03 == hn_state_buf[0]) || (0x04 == hn_state_buf[0])
			    || (0x07 == hn_state_buf[0])) {
				GTP_DEBUG("Wakeup HN_MASTER_SEND block polling waiter");
				got_hotknot_state |= HN_MASTER_SEND;
				got_hotknot_extra_state = hn_state_buf[0];
				wake_up_interruptible(&bp_waiter);
			}
		} else if (wait_hotknot_state & HN_SLAVE_RECEIVED) {
			if ((0x03 == hn_state_buf[1]) || (0x04 == hn_state_buf[1])
			    || (0x07 == hn_state_buf[1])) {
				GTP_DEBUG("Wakeup HN_SLAVE_RECEIVED block polling waiter:0x%x", hn_state_buf[1]);
				got_hotknot_state |= HN_SLAVE_RECEIVED;
				got_hotknot_extra_state = hn_state_buf[1];
				wake_up_interruptible(&bp_waiter);
			}
		} else if (wait_hotknot_state & HN_MASTER_DEPARTED) {
			if (0x07 == hn_state_buf[0]) {
				GTP_DEBUG("Wakeup HN_MASTER_DEPARTED block polling waiter");
				got_hotknot_state |= HN_MASTER_DEPARTED;
				wake_up_interruptible(&bp_waiter);
			}
		} else if (wait_hotknot_state & HN_SLAVE_DEPARTED) {
			if (0x07 == hn_state_buf[1]) {
				GTP_DEBUG("Wakeup HN_SLAVE_DEPARTED block polling waiter");
				got_hotknot_state |= HN_SLAVE_DEPARTED;
				wake_up_interruptible(&bp_waiter);
			}
		}
		return 0;
	}

	return -1;
}
#endif /*HOTKNOT_BLOCK_RW*/
#endif /*GTP_HOTKNOT*/

#define GOODIX_MAGIC_NUMBER        'G'
#define NEGLECT_SIZE_MASK           (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))

#define GESTURE_ENABLE              _IO(GOODIX_MAGIC_NUMBER, 1)
#define GESTURE_DISABLE             _IO(GOODIX_MAGIC_NUMBER, 2)
#define GESTURE_FLAG_SET            _IO(GOODIX_MAGIC_NUMBER, 3)
#define GESTURE_FLAG_CLEAR          _IO(GOODIX_MAGIC_NUMBER, 4)
/*#define SET_ENABLED_GESTURE         (_IOW(GOODIX_MAGIC_NUMBER, 5, u8) & NEGLECT_SIZE_MASK)*/
#define GESTURE_DATA_OBTAIN         (_IOR(GOODIX_MAGIC_NUMBER, 6, u8) & NEGLECT_SIZE_MASK)
#define GESTURE_DATA_ERASE          _IO(GOODIX_MAGIC_NUMBER, 7)

/*#define HOTKNOT_LOAD_SUBSYSTEM      (_IOW(GOODIX_MAGIC_NUMBER, 6, u8) & NEGLECT_SIZE_MASK)*/
#define HOTKNOT_LOAD_HOTKNOT        _IO(GOODIX_MAGIC_NUMBER, 20)
#define HOTKNOT_LOAD_AUTHENTICATION _IO(GOODIX_MAGIC_NUMBER, 21)
#define HOTKNOT_RECOVERY_MAIN       _IO(GOODIX_MAGIC_NUMBER, 22)
/*#define HOTKNOT_BLOCK_RW            (_IOW(GOODIX_MAGIC_NUMBER, 6, u8) & NEGLECT_SIZE_MASK)*/
#define HOTKNOT_DEVICES_PAIRED      _IO(GOODIX_MAGIC_NUMBER, 23)
#define HOTKNOT_MASTER_SEND         _IO(GOODIX_MAGIC_NUMBER, 24)
#define HOTKNOT_SLAVE_RECEIVE       _IO(GOODIX_MAGIC_NUMBER, 25)
/*#define HOTKNOT_DEVICES_COMMUNICATION*/
#define HOTKNOT_MASTER_DEPARTED     _IO(GOODIX_MAGIC_NUMBER, 26)
#define HOTKNOT_SLAVE_DEPARTED      _IO(GOODIX_MAGIC_NUMBER, 27)
#define HOTKNOT_VENDOR_VERSION      (_IOR(GOODIX_MAGIC_NUMBER, 28, u8) & NEGLECT_SIZE_MASK)
#define HOTKNOT_WAKEUP_BLOCK        _IO(GOODIX_MAGIC_NUMBER, 29)

#define IO_IIC_READ                  (_IOR(GOODIX_MAGIC_NUMBER, 100, u8) & NEGLECT_SIZE_MASK)
#define IO_IIC_WRITE                 (_IOW(GOODIX_MAGIC_NUMBER, 101, u8) & NEGLECT_SIZE_MASK)
#define IO_RESET_GUITAR              _IO(GOODIX_MAGIC_NUMBER, 102)
#define IO_DISABLE_IRQ               _IO(GOODIX_MAGIC_NUMBER, 103)
#define IO_ENABLE_IRQ                _IO(GOODIX_MAGIC_NUMBER, 104)
#define IO_GET_VERISON               (_IOR(GOODIX_MAGIC_NUMBER, 110, u8) & NEGLECT_SIZE_MASK)
#define IO_PRINT                     (_IOW(GOODIX_MAGIC_NUMBER, 111, u8) & NEGLECT_SIZE_MASK)
#define IO_VERSION                   "V1.3-20150420"

#define CMD_HEAD_LENGTH             20
static s32 io_iic_read(u8 *data, void __user *arg)
{
	s32 err = ERROR;
	s32 data_length = 0;
	u16 addr = 0;

	err = copy_from_user(data, arg, CMD_HEAD_LENGTH);
	if (err) {
		GTP_ERROR("Can't access the memory.");
		return ERROR_MEM;
	}

	addr = data[0] << 8 | data[1];
	data_length = data[2] << 8 | data[3];

	err = gt1x_i2c_read(addr, &data[CMD_HEAD_LENGTH], data_length);
	if (!err) {
		err = copy_to_user(&((u8 __user *) arg)[CMD_HEAD_LENGTH], &data[CMD_HEAD_LENGTH], data_length);
		if (err) {
			GTP_ERROR("ERROR when copy to user.[addr: %04x], [read length:%d]", addr, data_length);
			return ERROR_MEM;
		}
		err = CMD_HEAD_LENGTH + data_length;
	}
	/*GTP_DEBUG("IIC_READ.addr:0x%4x, length:%d, ret:%d", addr, data_length, err);*/
	/*GTP_DEBUG_ARRAY((&data[CMD_HEAD_LENGTH]), data_length);*/

	return err;
}

static s32 io_iic_write(u8 *data)
{
	s32 err = ERROR;
	s32 data_length = 0;
	u16 addr = 0;

	addr = data[0] << 8 | data[1];
	data_length = data[2] << 8 | data[3];

	err = gt1x_i2c_write(addr, &data[CMD_HEAD_LENGTH], data_length);
	if (!err) {
		err = CMD_HEAD_LENGTH + data_length;
	}

	return err;
}

/*
 *@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;
	u8 *data = NULL;
	int cnt = 30;

	/* Blocking when firmwaer updating */
	while (cnt-- && update_info.status) {
		ssleep(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_ERROR("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_irq_disable();
		gt1x_reset_guitar();
		gt1x_irq_enable();
		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:
		GTP_DEBUG("Gesture switch ON.");
		gesture_enabled = 1;
		break;

	case GESTURE_DISABLE:
		GTP_DEBUG("Gesture switch OFF.");
		gesture_enabled = 0;
		break;

	case GESTURE_FLAG_SET:
		SETBIT(gestures_flag, (u8) value);
		GTP_DEBUG("Gesture flag: 0x%02X enabled.", value);
		break;

	case GESTURE_FLAG_CLEAR:
		CLEARBIT(gestures_flag, (u8) value);
		GTP_DEBUG("Gesture flag: 0x%02X disabled.", value);
		break;

	case GESTURE_DATA_OBTAIN:
		GTP_DEBUG("Obtain gesture data.");
		mutex_lock(&gesture_data_mutex);
		ret = copy_to_user(((u8 __user *) arg), &gesture_data.data, 4 + gesture_data.data[1] * 4 + gesture_data.data[3]);
		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];
		}
		mutex_unlock(&gesture_data_mutex);
		break;

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

#if GTP_HOTKNOT
	case HOTKNOT_VENDOR_VERSION:
		ret =  copy_to_user(((u8 __user *) arg), HOTKNOT_VERSION, sizeof(HOTKNOT_VERSION));
		if (!ret) {
			ret = sizeof(HOTKNOT_VERSION);
		}
		break;
	case HOTKNOT_LOAD_HOTKNOT:
		ret = hotknot_load_hotknot_subsystem();
		break;

	case HOTKNOT_LOAD_AUTHENTICATION:
#if GTP_ESD_PROTECT
		gt1x_esd_switch(SWITCH_OFF);
#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;
}

#ifdef CONFIG_COMPAT
static long gt1x_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *arg32 = compat_ptr(arg);

	if (!file->f_op || !file->f_op->unlocked_ioctl)
		return -ENOTTY;

	return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
}
#endif

static const struct file_operations gt1x_fops = {
	.owner = THIS_MODULE,
#if GTP_GESTURE_WAKEUP
	.read = gt1x_gesture_data_read,
	.write = gt1x_gesture_data_write,
#endif
	.unlocked_ioctl = gt1x_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = gt1x_compat_ioctl,
#endif
};

#if GTP_HOTKNOT
static const struct file_operations hotknot_fops = {
	.open = hotknot_open,
	.release = hotknot_release,
	.unlocked_ioctl = gt1x_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = gt1x_compat_ioctl,
#endif
};

static struct miscdevice hotknot_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = HOTKNOT_NODE,
	.fops = &hotknot_fops,
};
#endif

s32 gt1x_init_node(void)
{
#if GTP_GESTURE_WAKEUP
	struct proc_dir_entry *proc_entry = NULL;
	mutex_init(&gesture_data_mutex);
	memset(gestures_flag, 0, sizeof(gestures_flag));
	memset((u8 *) &gesture_data, 0, sizeof(st_gesture_data));

	proc_entry = proc_create(GESTURE_NODE, 0755, NULL, &gt1x_fops);
	if (proc_entry == NULL) {
		GTP_ERROR("CAN't create proc entry /proc/%s.", GESTURE_NODE);
		return -1;
	} else {
		GTP_INFO("Created proc entry /proc/%s.", GESTURE_NODE);
	}
#endif

#if GTP_HOTKNOT
	if (misc_register(&hotknot_misc_device)) {
		GTP_ERROR("CAN't create misc device in /dev/hotknot.");
		return -1;
	} else {
		GTP_INFO("Created misc device in /dev/hotknot.");
	}
#endif
	return 0;
}

void gt1x_deinit_node(void)
{
#if GTP_GESTURE_WAKEUP
	remove_proc_entry(GESTURE_NODE, NULL);
#endif

#if GTP_HOTKNOT
	misc_deregister(&hotknot_misc_device);
#endif
}