/* 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 #include #include #include #include #include #include #include #include #include #include #include #include /*proc */ #include #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, >1x_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 }