summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/gt1151/gt1x_extents.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/gt1151/gt1x_extents.c')
-rw-r--r--drivers/input/touchscreen/gt1151/gt1x_extents.c932
1 files changed, 932 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/gt1151/gt1x_extents.c b/drivers/input/touchscreen/gt1151/gt1x_extents.c
new file mode 100644
index 000000000000..d0d215cc30a3
--- /dev/null
+++ b/drivers/input/touchscreen/gt1151/gt1x_extents.c
@@ -0,0 +1,932 @@
+/* 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/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 <asm/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 = 0; /* 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;
+ }
+ msleep(10);
+ }
+ 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 = 0, err_flag2 = 0;
+ 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 = 0;
+u8 hotknot_transfer_mode = 0;
+
+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 = 0;
+static u8 got_hotknot_extra_state = 0;
+static u8 wait_hotknot_state = 0;
+static u8 force_wake_flag = 0;
+static u8 block_enable = 0;
+s32 hotknot_paired_flag = 0;
+
+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 = 0;
+ 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) // 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;
+ }
+
+ //GTP_DEBUG("IIC_WRITE.addr:0x%4x, length:%d, ret:%d", addr, data_length, err);
+ //GTP_DEBUG_ARRAY((&data[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; //the initial value must be 0
+ u8 *data = NULL;
+
+#if GTP_AUTO_UPDATE
+ int cnt = 30;
+ /* Blocking when firmwaer updating */
+ while (cnt-- && update_info.status) {
+ ssleep(1);
+ }
+#endif
+
+ //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_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, 0666, 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
+}