Message ID | 1350424679-20725-1-git-send-email-scott.liu@emc.com.tw (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Scott, Some comments and questions below, Thanks, -JJ Scott Liu <scott.liu@emc.com.tw> writes: > This patch is for Elan eKTF Touchscreen product, I2C adpater module. > > This driver adds communication support with Elan eKTF controller using I2C bus. > > Signed-off-by: Scott Liu <scott.liu@emc.com.tw> > --- > drivers/input/touchscreen/Kconfig | 9 + > drivers/input/touchscreen/Makefile | 1 + > drivers/input/touchscreen/elants_i2c.c | 3182 ++++++++++++++++++++++++++++++++ > include/linux/i2c/elants.h | 61 + > 4 files changed, 3253 insertions(+) > create mode 100644 drivers/input/touchscreen/elants_i2c.c > create mode 100644 include/linux/i2c/elants.h > > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig > index 1ba232c..50e6f05 100644 > --- a/drivers/input/touchscreen/Kconfig > +++ b/drivers/input/touchscreen/Kconfig > @@ -237,6 +237,15 @@ config TOUCHSCREEN_EETI > To compile this driver as a module, choose M here: the > module will be called eeti_ts. > > +config TOUCHSCREEN_ELAN > + tristate "Elan touchscreen panel support" > + depends on I2C > + help > + Say Y here to enable support for I2C connected Elan touch panels. > + > + To compile this driver as a module, choose M here: the > + module will be called elants_i2c. > + > config TOUCHSCREEN_EGALAX > tristate "EETI eGalax multi-touch panel support" > depends on I2C > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile > index 178eb12..428a631 100644 > --- a/drivers/input/touchscreen/Makefile > +++ b/drivers/input/touchscreen/Makefile > @@ -28,6 +28,7 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o > obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o > obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o > obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o > +obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o > obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o > obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o > obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o > diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c > new file mode 100644 > index 0000000..d87b2ef > --- /dev/null > +++ b/drivers/input/touchscreen/elants_i2c.c > @@ -0,0 +1,3182 @@ > +/* > + * Elan Microelectronics touchpanels with I2C interface > + * > + * Copyright (C) 2012 Elan Microelectronics Corporation. > + * Scott Liu <scott.liu@emc.com.tw> > + * > + * This code is partly based on hid-multitouch.c: > + * > + * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> > + * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> > + * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France > + * > + */ > + > +/* > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + */ > + > +#define CHIP_NUM 2 What does this constant mean? What chip does this refer to? > + > +/* Reserved for having platform to test */ > +#define MT_TYPE_B 0 Can't we just go MT-B all the way? Reserved to test what? > +/* Define it if using on Android system */ > +#define ANDROID 0 Please just target mainline. Or at least make this a Kconfig option and add a second patch for Android. > +#include <linux/module.h> > +#include <linux/input.h> > +#include <linux/interrupt.h> > + > +#include <linux/platform_device.h> > +#include <linux/i2c.h> > +#include <linux/delay.h> > +#include <linux/gpio.h> > +#include <linux/jiffies.h> > +#include <linux/hrtimer.h> > +#include <linux/timer.h> > +#include <linux/kthread.h> > +#include <linux/miscdevice.h> > +#include <linux/uaccess.h> > +#include <linux/buffer_head.h> > +#include <linux/version.h> > +#include <linux/kfifo.h> > +#include <linux/slab.h> > +#if (ANDROID) Please drop this. > +#include <linux/wakelock.h> > +#include <linux/earlysuspend.h> > +#endif > +#if (MT_TYPE_B) I definitely think we should always use MT-B. > +#include <linux/input/mt.h> > +#endif > + > +/* debug option */ > +static bool debug = false; > +module_param(debug, bool, 0444); > +MODULE_PARM_DESC(debug, "print a lot of debug information"); > + > +#define elan_dbg(client, fmt, arg...) \ > + if (debug) \ > + dev_printk(KERN_DEBUG, &client->dev, fmt, ##arg) > + > +/*================================================= > + * Marco > + *================================================= */ > +#define DRV_NAME "elants_i2c" > + > +#define DRV_MA_VER 2 > +#define DRV_MI_VER 0 > +#define DRV_SUB_MI_VER 0 > + > +#define _str(s) #s > +#define str(s) _str(s) > +#define DRIVER_VERSION str(DRV_MA_VER.DRV_MI_VER.DRV_SUB_MI_VER) > + > + > +#define IDX_PACKET_SIZE_4FIG 17 > +#define IDX_PACKET_SIZE_WIDTH 40 > + > +#define FINGER_NUM 10 > + > +#define PWR_STATE_DEEP_SLEEP 0 > +#define PWR_STATE_NORMAL 1 > +#define PWR_STATE_MASK BIT(3) > + > +#define CMD_S_PKT 0x52 > +#define CMD_R_PKT 0x53 > +#define CMD_W_PKT 0x54 > + > +#define HELLO_PKT 0x55 > +#define NORMAL_PKT 0x62 > + > +#define RESET_PKT 0x77 > +#define CALIB_PKT 0xA8 > + > +#define FINGER_ID 1 > + > +#define FIFO_SIZE (64) > + > + > +/*! Convert from rows or columns into resolution */ > +#define ELAN_TS_RESOLUTION(n) ((n - 1) * 64) > + > +static const char hello_packet[4] = { 0x55, 0x55, 0x55, 0x55 }; > +static const char iniap_packet[4] = { 0x55, 0xaa, 0x33, 0xcc }; > +static const char recov_packet[4] = { 0x55, 0x55, 0x80, 0x80 }; > + > +#include <linux/i2c/elants.h> Move this up with the rest of the includes. > +/*! Firmware protocol status flag */ > +#define PRO_I2C_WRT_CMD_SYNC 0x00000001 > +#define PRO_HID_MOD_CHECKSUM 0x00000002 > +#define PRO_UPDATE_FW_MODE 0x00000080 > + > + > +/*! driver status flag, should move to public header file */ > +#define STA_NONINIT 0x00000001 > +#define STA_INIT 0x00000002 > +#define STA_INIT2 0x00000004 > +#define STA_INIT3 0x00000100 > +#define STA_INIT4 0x00000200 > +#define STA_PROBED 0x00000008 > +#define STA_ERR_HELLO_PKT 0x00000010 > +#define STA_USE_IRQ 0x00000020 > +#define STA_SLEEP_MODE 0x00000040 > + > +/*======================================= > + * Structure Definition > + *=======================================*/ > + > +/*! @enum elan i2c address definition */ > +enum elan_i2c_addr { > + elan_i2c_master = 0x10, > + elan_i2c_slave1 = 0x20, > + elan_i2c_slave2 = 0x21, > + > + elan_i2c_maxnum = 3, > +}; > + > +/*! @enum finger_report_header Finger report header byte definition */ > +enum finger_report_header { > + idx_coordinate_packet_4_finger = 0x5c, > + idx_coordinate_packet_10_finger = 0x62, > +}; > + > +/*! @enum fw_queue_report_hdr FW Queue report header definition */ > +enum fw_queue_report_hdr { > + queue_header_byte_Single = 0x62, > + queue_header_byte_normal = 0x63, > + queue_header_byte_wait = 0x64, > + queue_header_size = 4, > + queue_packet_max_len = queue_header_size + (IDX_PACKET_SIZE_WIDTH * 3), > +}; > + > +/*! @enum fw_normal_cmd_hdr FW Normal command header definition */ > +enum fw_normal_cmd_hdr { > + cmd_header_byte_write = 0x54, > + cmd_header_byte_read = 0x53, > + cmd_header_byte_response = 0x52, > + cmd_header_byte_hello = 0x55, > + cmd_response_len = 4 > +}; > + > +/*! @enum fw_info_pos FW information position */ > +enum fw_info_pos { > + idx_finger_header = 0, > + idx_finger_state = 1, > + idx_finger_total = 2, > + idx_finger_checksum = 34, > + idx_finger_width = 35, > + idx_4finger_checksum = 17, > + idx_finger_width_checksum = 34, > +}; Seems odd to define these as an enum. Define constants? > +/*! @enum lock_bit Lock bit definition */ > +enum lock_bit { > + idx_file_operate = 0, > + idx_cmd_handshake = 1, > + idx_finger_report = 2, > + idx_test_mode = 3, > +}; > + > +enum kfifo_ret { > + ret_ok = 0, > + ret_cmdrsp = 1, > + ret_fail = -1, > +}; > + > +typedef enum elan_boot_e { > + E_BOOT_NORM = 0, > + E_BOOT_IAP = 1 > +} elan_boot_t; > + > +/* FW read command, 0x53 0x?? 0x0, 0x01 */ > +enum { > + E_ELAN_INFO_FW_VER = 0x00, > + E_ELAN_INFO_BC_VER = 0x10, > + E_ELAN_INFO_FW_ID = 0xf0 > +}; > + > + > +/*! @struct <multi_queue_header> */ > +struct multi_queue_header { > + u8 packet_id; > + u8 report_count; > + u8 report_length; > + u8 reserved; > +}; > + > +/*! @brief elan_polling */ > +struct elan_polling { > + struct workqueue_struct *elan_wq; /* polling work queue */ > + struct timer_list timer; /* Polling intr_gpio timer */ > + u8 int_status; /* polling intr gpio status */ > +}; > + > + > +/* finger handler, refer to hid-multitouch.c */ > +struct mt_slot { > + __s32 x, y, p, w, h; > + __s32 contactid; /* the device ContactID assigned to this slot */ > + bool touch_state; /* is the touch valid? */ > + bool seen_in_this_frame;/* has this slot been updated */ > +}; > + > +struct mt_device { > + struct mt_slot curdata; /* placeholder of incoming data */ > + __u8 num_received; /* how many contacts we received */ > + __u8 num_expected; /* expected last contact index */ > + __u8 maxcontacts; > + bool curvalid; /* is the current contact valid? */ > + struct mt_slot *slots; > +}; With Benjamin's i2c-hid implimentation, is it possible to make hid-multitouch not depend on USBHID and reuse it to drive this device? > + > +/** > +* > +* @brief elants_data > +* > +* all variable should include in this struct. > +* > +*/ > +struct elants_data { > + int intr_gpio; /* interupter pin*/ > + int rst_gpio; /* reset pin*/ > + int use_irq; > + u8 major_fw_version; > + u8 minor_fw_version; > + u8 major_bc_version; > + u8 minor_bc_version; > + u8 major_hw_id; > + u8 minor_hw_id; > + bool fw_enabled; /* True if firmware device enabled*/ > + int rows; /* Panel geometry for input layer*/ > + int cols; > + int x_max; > + int y_max; > + /* Power state 0:sleep 1:active*/ > + u8 power_state; > + /* TS is in IAP mode already*/ > +#define IAP_MODE_ENABLE 1 Why not add this as a Kconfig entry? > + /* 1 : Firmware update mode > + 0 : normal*/ > + unsigned int iap_mode; > + unsigned int rx_size; /* Read size in use*/ > + > + /* Multi-queue info */ > + struct multi_queue_header mq_header; > + > + /* our i2c client*/ > + struct i2c_client *client; > + /* input device*/ > + struct input_dev *input; > + /* normal function work thread*/ > + struct workqueue_struct *elan_wq; > + /* normal function work queue*/ > + struct work_struct work; > + /* start probe start*/ > + struct task_struct *thread; > + /* char device for ioctl and IAP*/ > + struct miscdevice firmware; > + /* use timer if use_irq == 0*/ > + struct hrtimer timer; > + /* regular polling work thread*/ > + struct work_struct pollingwork; > + /* regular polling work queue*/ > + struct elan_polling polling; > +#if (ANDROID) > + struct early_suspend early_suspend; > + /* avoid sleep during IAP.*/ > + struct wake_lock wakelock; > +#endif Please drop this. > + /* Protects I2C accesses to device*/ > + struct mutex mutex; > + /* Protects I2C tx/rx*/ > + struct mutex tr_mutex; > + > + /* Lock openers*/ > + unsigned long busy; > + /* Protocol stats for firmware*/ > + unsigned int protocol; > + > + /* elan-iap i2c address*/ > + unsigned short i2caddr; > + > + /* fifo and processing */ > + struct kfifo fifo; > + > + /* Serialize operations around FIFO */ > + struct mutex fifo_mutex; > + wait_queue_head_t wait; > + spinlock_t rx_kfifo_lock; > + > + /* boot log */ > + u8 boot_log[256]; > + > + /*! Add for TS driver debug */ > + unsigned int status; > + long int irq_received; > + long int packet_received; > + long int packet_fail; > + long int touched_sync; > + long int no_touched_sync; > + long int checksum_correct; > + long int wdt_reset; > + u16 checksum_fail; > + u16 header_fail; > + u16 mq_header_fail; > + u16 drop_frame; > + > + /* mt device */ > + struct mt_device td; > +}; > + > + > + > +/*/============================================ > + * Function prototype > + *=============================================*/ > +static int elants_set_data(struct i2c_client *client, > + const u8 *data, > + size_t len); > +static int elants_async_recv_data(struct i2c_client *client, > + const uint8_t *cmd, > + uint8_t *buf, > + size_t tx_size, > + size_t rx_size); > + > +static void elants_drop_packet(struct elants_data *ts); > +static irqreturn_t elants_work_func(int irq, void *work); > + > +static irqreturn_t elants_irq_handler(int irq, void *dev_id); > +static int elan_hw_reset(struct i2c_client *client); > +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf); > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > +static void elants_early_suspend(struct early_suspend *h); > +static void elants_late_resume(struct early_suspend *h); > +#endif Android specific. Please drop. > + > +/*================================================= > + * Global variable > + *=================================================*/ > + > +/*! for elan-iap char driver */ > +static struct miscdevice *private_ts; Firmware updating functionality? What about try to use sysfs entry? And it would be easier to review if you split firmware updating code into another patch. > +static bool old_bootcode = false; > + > +static u8 tp_device_addr[3] = {elan_i2c_master, > + elan_i2c_slave1, > + elan_i2c_slave2 > + }; > + > + > +#define elants_acqurie_data(a, cmd, buf, c) \ > + elants_async_recv_data(a, cmd, buf, c, c) > + > +/*================================================= > + * Function implement > + *=================================================*/ > +static inline void elan_msleep(u32 t) > +{ > + /* > + * If the sleeping time is 10us - 20ms, usleep_range() is recommended. > + * Read Documentation/timers/timers-howto.txt > + */ > + usleep_range(t*1000, t*1000 + 500); > +} > + > +/** > + * @brief __issue_bootcode_cmd - hadle bootcode status. > + * @param client : our i2c device > + * > + * @return { >0 means success, > + * otherwise fail. } > + * > + * Enter Normal mode packet is {0x55, 0x55, 0x55, 0x55} > + * 2012/3/1 won't issue bootcmd in normal driver initial flow. > + */ > +static int elan_bootcode_cmd(struct i2c_client *client, > + u8 passcode) > +{ > + int rc = 0; > + struct elants_data *ts = i2c_get_clientdata(client); > + u8 command[2][4] = { > + {0x45, 0x49, 0x41, 0xFF}, /* normal_command */ > + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ > + }; > + > + /* Detect old / new FW */ > + elan_msleep(90); > + > + if (gpio_get_value(ts->intr_gpio) == 0) { > + /* Old FW */ > + pr_warn("detect intr!!!!! OLD BC detect\n"); > + return -2; > + } > + > + if (passcode > E_BOOT_IAP) > + return -1; > + > + elan_dbg(client, "passcode = %x:%x:%x:%x\n", > + command[passcode][0], > + command[passcode][1], > + command[passcode][2], > + command[passcode][3]); > + > + mutex_lock(&ts->mutex); > + > + ts->i2caddr = elan_i2c_slave2; > + rc = elants_set_data(client, command[passcode], 4); > + if (rc < 0) > + dev_err(&ts->client->dev, > + "[ELAN] set passcode[%d] fail\n", passcode); > + > + ts->i2caddr = elan_i2c_slave1; > + rc = elants_set_data(client, command[passcode], 4); > + if (rc < 0) > + dev_err(&ts->client->dev, > + "[ELAN] set passcode[%d] fail\n", passcode); > + > + ts->i2caddr = elan_i2c_master; > + rc = elants_set_data(client, command[passcode], 4); > + if (rc < 0) > + dev_err(&ts->client->dev, > + "[ELAN] set passcode[%d] fail\n", passcode); > + > + /* normal communitcat interactives with Master. */ > + ts->i2caddr = elan_i2c_master; > + mutex_unlock(&ts->mutex); > + > + return rc; > +} > + > +/** > +* @brief \b elan_iap_open - char device > +* > +* purpose for ioctl and iap updating fw. > +*/ > +int elan_iap_open(struct inode *inode, struct file *filp) > +{ > + struct elants_data *ts ; > + > + filp->private_data = private_ts; > + ts = container_of(filp->private_data, > + struct elants_data, firmware); > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + return 0; > +} > + > +/** > +* @brief \b elan_iap_release - char device > +* > +* purpose for close this device > +*/ > +int elan_iap_release(struct inode *inode, struct file *filp) > +{ > + struct elants_data *ts = > + container_of(filp->private_data, > + struct elants_data, firmware); > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + return 0; > +} > + > +/** > +* @brief \b elan_iap_write - iap write page data > +* @param buff is page data from user space > +* @param count is number of data in byte. > +* > +* purpose for iap write page data > +*/ > +ssize_t elan_iap_write(struct file *filp, > + const char *buff, > + size_t count, > + loff_t *offp) > +{ > + struct elants_data *ts = > + container_of(filp->private_data, > + struct elants_data, firmware); > + int ret; > + u8 txbuf[256]; > + struct i2c_adapter *adap = ts->client->adapter; > + struct i2c_msg msg; > + > + if (count > 256) > + return -EMSGSIZE; > + > + if (copy_from_user(txbuf, buff, count)) > + return -EFAULT; > + > + msg.addr = ts->i2caddr; > + msg.flags = ts->client->flags & I2C_M_TEN; > + msg.len = count; > + msg.buf = (char *)txbuf; > + > + ret = i2c_transfer(adap, &msg, 1); > + if (ret != 1) > + dev_err(&ts->client->dev, > + "[ELAN] i2c_master_send fail, ret=%d\n", ret); > + > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > + transmitted, else error code. */ > + return (ret == 1) ? count : ret; > +} > + > +/** > +* @brief \b elan_iap_read - read status code from TP > +* > +* purpose for iap read status code from TP > +*/ > +ssize_t elan_iap_read(struct file *filp, > + char *buff, > + size_t count, > + loff_t *offp) > +{ > + struct elants_data *ts = > + container_of(filp->private_data, > + struct elants_data, firmware); > + u8 rxbuf[256]; > + int ret; > + struct i2c_adapter *adap = ts->client->adapter; > + struct i2c_msg msg; > + > + if (count > 256) > + return -EMSGSIZE; > + > + msg.addr = ts->i2caddr; > + msg.flags = ts->client->flags & I2C_M_TEN; > + msg.flags |= I2C_M_RD; > + msg.len = count; > + msg.buf = rxbuf; > + > + ret = i2c_transfer(adap, &msg, 1); > + if (ret == 1) > + if (copy_to_user(buff, rxbuf, count)) > + return -EFAULT; > + > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > + transmitted, else error code. */ > + return (ret == 1) ? count : ret; > +} > + > +/** > +* @brief \b elan_iap_ioctl - ioctl > +* @param cmd to control our TP device. > +* @param arg is parameter from user space > +* > +* purpose is that control our TP by char device node. > +*/ > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) > +static int elan_iap_ioctl(struct inode *inode, > + struct file *filp, > + unsigned int cmd, > + unsigned long arg) > +#else > +static long elan_iap_ioctl(struct file *filp, > + unsigned int cmd, > + unsigned long arg) > +#endif > +{ > + struct elants_data *ts = > + container_of(filp->private_data, > + struct elants_data, firmware); > + int __user *argp = (int __user *)arg; > + u8 len, buf[32]; > + int rc; > + > + switch (cmd) { > + case IOCTL_I2C_SLAVE: > + ts->i2caddr = (unsigned short)arg; > + break; > + case IOCTL_MAJOR_FW_VER: > + put_user(ts->major_fw_version, argp); > + break; > + case IOCTL_MINOR_FW_VER: > + put_user(ts->minor_fw_version, argp); > + break; > + case IOCTL_CHECK_RECOVERY_MODE: > + put_user(ts->iap_mode, argp); > + break; > + case IOCTL_IAP_ENABLE: { > + disable_irq(ts->client->irq); > +#if (ANDROID) > + wake_lock(&ts->wakelock); > +#endif > + ts->protocol |= PRO_UPDATE_FW_MODE; > + } > + break; > + case IOCTL_IAP_DISABLE: > + ts->protocol &= ~PRO_UPDATE_FW_MODE; > +#if (ANDROID) > + wake_unlock(&ts->wakelock); > +#endif > + enable_irq(ts->client->irq); > + break; > + case IOCTL_BOOTCODE_CMD: { > + int mode; > + > + len = 1; > + if (copy_from_user(buf, (const void *)arg, len)) > + return -EFAULT; > + > + mode = buf[0]; > + rc = elan_bootcode_cmd(ts->client, (u8)mode); > + if (rc < 0) { > + elan_dbg(ts->client, > + "elan_bootcode_command error\n"); > + return -1; > + } > + } > + break; > + case IOCTL_RESET: > + pr_warn("[ELAN] IOCTL_RESET\n"); > + rc = elan_hw_reset(ts->client); > + if (rc < 0) { > + elan_dbg(ts->client, > + "[ELAN] elan_hw_reset error\n"); > + return -1; > + } > + break; > + case IOCTL_I2C_INT: > + put_user(gpio_get_value(ts->intr_gpio), argp); > + break; > + case IOCTL_SET_COMMAND: { > + struct i2c_client *client = ts->client; > + struct elan_ioctl_data idata; > + > + if (copy_from_user(&idata.len, > + (const void *)arg, > + sizeof(idata.len))) { > + dev_err(&client->dev, > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > + buf[0], buf[1], buf[2], buf[3]); > + return -EFAULT; > + } > + > + if (copy_from_user(&idata, > + (const void *)arg, > + idata.len + sizeof(int))) { > + dev_err(&client->dev, > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > + buf[0], buf[1], buf[2], buf[3]); > + return -EFAULT; > + } > + dev_dbg(&ts->client->dev, > + "IOCTL_SET_COMMAND:%x:%x:%x:%x\n", > + idata.buf[0], > + idata.buf[1], > + idata.buf[2], > + idata.buf[3]); > + > + if (idata.buf[0] != CMD_R_PKT && > + idata.buf[0] != CMD_W_PKT) > + return -EFAULT; > + > + mutex_lock(&ts->mutex); > + elants_set_data(ts->client, > + (const u8 *)idata.buf, > + idata.len); > + mutex_unlock(&ts->mutex); > + } > + break; > + case IOCTL_GET_COMMAND: { > + int rc = 0; > + struct i2c_client *client = ts->client; > + struct elan_ioctl_data idata; > + > + /* Get command length first */ > + if (copy_from_user(&idata.len, > + (const void *)arg, > + sizeof(idata.len))) { > + dev_err(&client->dev, > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > + buf[0], buf[1], buf[2], buf[3]); > + return -EFAULT; > + } > + > + /* Get command real length then */ > + if (copy_from_user(&idata, (const void *)arg, > + idata.len + sizeof(int))) { > + dev_err(&client->dev, > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > + buf[0], buf[1], buf[2], buf[3]); > + return -EFAULT; > + } > + > + if (idata.buf[0] != CMD_R_PKT && idata.buf[0] != CMD_W_PKT) > + return -EFAULT; > + > + pr_info("[ELAN] IOCTL_GET_COMMAND %x:%x:%x:%x\n", > + idata.buf[0], idata.buf[1], idata.buf[2], idata.buf[3]); > + > + mutex_lock(&ts->mutex); > + > + set_bit(idx_cmd_handshake, &ts->busy); > + > + elants_set_data(client, idata.buf, idata.len); > + mutex_unlock(&ts->mutex); > + > + /* We will wait for non O_NONBLOCK handles until a signal or data */ > + mutex_lock(&ts->fifo_mutex); > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + while (kfifo_len(ts->fifo) == 0) { > +#else > + while (kfifo_len(&ts->fifo) == 0) { > +#endif > + mutex_unlock(&ts->fifo_mutex); > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + rc = wait_event_interruptible_timeout( > + ts->wait, kfifo_len(ts->fifo), > + msecs_to_jiffies(3000)); > +#else > + rc = wait_event_interruptible_timeout( > + ts->wait, kfifo_len(&ts->fifo), > + msecs_to_jiffies(3000)); > +#endif Please drop these CPP version magic. Target only current mainline kernel. > + if (rc <= 0) { > + rc = -ETIMEDOUT; > + dev_err(&client->dev, > + "timeout!! wake_up(ts->wait)\n"); > + goto err2; > + } > + mutex_lock(&ts->fifo_mutex); > + } > + if (elan_touch_pull_frame(ts, idata.buf) < 0) { > + rc = -1; > + goto err1; > + } > + > +err1: > + mutex_unlock(&ts->fifo_mutex); > +err2: > + clear_bit(idx_cmd_handshake, &ts->busy); > + > + dev_dbg(&client->dev, > + "[ELAN] Leave %s\n", __func__); > + > + if (rc < 0) { > + rc = copy_to_user((void __user *)arg, > + (void *)&idata, > + 4+sizeof(len)); > + return -rc; > + } > + > + elan_dbg(client, > + "Reponse=%x:%x:%x:%x\n", > + idata.buf[0], > + idata.buf[1], > + idata.buf[2], > + idata.buf[3]); > + > + if (copy_to_user((void __user *)arg, > + (void *)&idata, > + sizeof(idata))) > + return -EFAULT; > + > + } > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static const struct file_operations elan_touch_fops = { > + .owner = THIS_MODULE, > + .open = elan_iap_open, > + .write = elan_iap_write, > + .read = elan_iap_read, > + .release = elan_iap_release, > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) > + .ioctl = elan_iap_ioctl, > +#else > + .unlocked_ioctl = elan_iap_ioctl, > +#endif > +}; Please drop this. Use sysfs and target mainline. > +/** > + * @brief interfaces > + * provide the hardware and firmware information > + */ > +static ssize_t show_fw_version_value( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + return sprintf(buf, "%.2x %.2x\n", > + ts->major_fw_version, > + ts->minor_fw_version); > +} > + > + > +static ssize_t show_bc_version_value( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + return sprintf(buf, "%.2x %.2x\n", > + ts->major_bc_version, > + ts->minor_bc_version); > +} > + > +static ssize_t show_drvver_value( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%s\n", DRIVER_VERSION); > +} > + > + > +static ssize_t show_intr_gpio( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + int ret = 0; > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + ret = gpio_get_value(ts->intr_gpio); > + > + return sprintf(buf, "%d\n", ret); > +} > + > +static ssize_t show_adapter_pkt_rvd( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + return sprintf(buf, "irq_received=%ld packet_received=%ld packet_fail=%ld \ > + mq_hdr_fail=%d, checksum_fail=%x header_fail=%d, \ > + poll_timer=%d, wdt_reset=%ld\n", > + ts->irq_received, ts->packet_received, > + ts->packet_fail, ts->mq_header_fail, > + ts->checksum_fail, ts->header_fail, > + ts->drop_frame, ts->wdt_reset); > +} > + > +static ssize_t show_queue_count( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + return sprintf(buf, "queue_report_count=%d, report_length=%d\n", > + ts->mq_header.report_count, ts->mq_header.report_length); > +} > + > + > +static ssize_t store_power_mode( > + struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + int ret = 0; > + unsigned long val; > + const char AcMode[] = {0x54, 0x30, 0x00, 0x01}; > + const char BtMode[] = {0x54, 0x38, 0x00, 0x01}; > + char cmd[4]; > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + ret = kstrtoul(buf, 10, &val); > + if (ret) > + return ret; > + > + switch (val) { > + case 0: > + memcpy(cmd, AcMode, sizeof(AcMode)); > + break; > + case 1: > + memcpy(cmd, BtMode, sizeof(BtMode)); > + break; > + default: > + return -1; > + } > + > + mutex_lock(&ts->mutex); > + elants_set_data(ts->client, cmd, sizeof(cmd)); > + mutex_unlock(&ts->mutex); > + > + return count; > +} > + > +static ssize_t show_power_mode( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + const char RespCmd[] = {0x53, 0x30, 0x00, 0x01}; > + char tbuf[4]; > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return -1; > + > + > + mutex_lock(&ts->mutex); > + set_bit(idx_cmd_handshake, &ts->busy); > + elants_acqurie_data(ts->client, RespCmd, tbuf, sizeof(RespCmd)); > + if (tbuf[0] != cmd_header_byte_response) { > + dev_err(&client->dev, > + "exception!! %x:%x:%x:%x\n", > + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); > + elants_drop_packet(ts); > + memset(tbuf, 0x0, sizeof(tbuf)); > + } > + clear_bit(idx_cmd_handshake, &ts->busy); > + mutex_unlock(&ts->mutex); > + > + return sprintf(buf, "%x:%x:%x:%x\n", > + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); > +} > + > +static ssize_t show_report_rate( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + long int old_frame, new_frame; > + > + old_frame = ts->packet_received; > + ssleep(1); > + new_frame = ts->packet_received; > + > + return sprintf(buf, "%ld\n", new_frame - old_frame); > +} > + > +static ssize_t show_iap_mode( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = > + container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + return sprintf(buf, "%s\n", > + (ts->iap_mode == 0) ? "Normal" : "Recovery"); > +} > + > +static ssize_t show_recal( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + u8 w_flashkey[4] = {0x54, 0xC0, 0xE1, 0x5A}; > + u8 rek[4] = {0x54, 0x29, 0x00, 0x01}; > + > + ts->i2caddr = elan_i2c_master; > + elants_set_data(client, w_flashkey, 4); > + elan_msleep(1); > + elants_set_data(client, rek, 4); > + > + return sprintf(buf, "%s\n", "re-K finish"); > +} > + > + > +static ssize_t show_boot_log( > + struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct elants_data *ts = i2c_get_clientdata(client); > + > + /* show boot status, allow user cat this log during iap mode enable!! */ > + > + return scnprintf(buf, PAGE_SIZE, ts->boot_log); > +} > + > + > +static DEVICE_ATTR(power_mode, S_IRUGO|S_IWUGO, > + show_power_mode, store_power_mode); > +static DEVICE_ATTR(queue_count, S_IRUGO, show_queue_count, NULL); > +static DEVICE_ATTR(drv_version, S_IRUGO, show_drvver_value, NULL); > +static DEVICE_ATTR(fw_version, S_IRUGO, show_fw_version_value, NULL); > +static DEVICE_ATTR(bc_version, S_IRUGO, show_bc_version_value, NULL); > +static DEVICE_ATTR(ts_packet, S_IRUGO, show_adapter_pkt_rvd, NULL); > +static DEVICE_ATTR(gpio, S_IRUGO, show_intr_gpio, NULL); > +static DEVICE_ATTR(report_rate, S_IRUGO, show_report_rate, NULL); > +static DEVICE_ATTR(boot_log, S_IRUGO, show_boot_log, NULL); > +static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL); > +static DEVICE_ATTR(rek, S_IRUGO, show_recal, NULL); > + > + > + > +static struct attribute *elan_attributes[] = { > + &dev_attr_power_mode.attr, > + &dev_attr_queue_count.attr, > + &dev_attr_fw_version.attr, > + &dev_attr_drv_version.attr, > + &dev_attr_bc_version.attr, > + &dev_attr_gpio.attr, > + &dev_attr_ts_packet.attr, > + &dev_attr_report_rate.attr, > + &dev_attr_boot_log.attr, > + &dev_attr_iap_mode.attr, > + &dev_attr_rek.attr, > + > + NULL > +}; > + > + > +static struct attribute_group elan_attribute_group = { > + .name = "elants", > + .attrs = elan_attributes, > +}; > + > + > +/** > + * @brief elan_hw_reset - h/w reset. > + * @param client : our i2c device > + * > + * @retval >0 means reset success, \n > + * otherwise is fail. > + * > + */ > +static int elan_hw_reset(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int ret; > + > + if (ts->rst_gpio < 0) > + return -1; > + > + ret = gpio_direction_output(ts->rst_gpio, 0); > + if (ret < 0) { > + pr_err("gpio_direction fail!\n"); > + return ret; > + } > + > + elan_msleep(2); > + > + ret = gpio_direction_output(ts->rst_gpio, 1); > + if (ret < 0) > + return ret; > + > + /* wait > 10ms to ensure that fw is in IAP mode */ > + msleep(20); > + > + return ret; > +} > + > + > + > +/** > + * @brief elan_ts_poll - polling intr pin status. > + * @param client : our i2c device > + * > + * @retval 0 means intr pin is low, \n > + * otherwise is high. > + * > + * polling intr pin untill low and the maximus wait time is \b 200ms. > + */ > +static int elan_ts_poll(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int status = 0, retry = 20; > + > + do { > + status = gpio_get_value(ts->intr_gpio); > + elan_dbg(client, > + "%s: status = %d\n", __func__, status); > + retry--; > + mdelay(10); > + } while (status != 0 && retry > 0); > + > + elan_dbg(client, > + "%s: poll interrupt status %s\n", > + __func__, status == 1 ? "high" : "low"); > + return (status == 0 ? 0 : -ETIMEDOUT); > +} > + > +/** > + * @brief \b elan_ts_set_data - set command to TP. > + * @param client : our i2c device > + * @param data : command or data which will send to TP. > + * @param len : command length usually. > + * > + * set command to our TP. > + */ > +static int elants_set_data(struct i2c_client *client, > + const u8 *data, size_t len) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + struct i2c_adapter *adap = client->adapter; > + struct i2c_msg msg; > + int rc = 0; > + > + dev_dbg(&client->dev, "[ELAN] Enter: %s\n", __func__); > + elan_dbg(client, > + "dump cmd: %02x, %02x, %02x, %02x, addr=%x\n", > + data[0], data[1], data[2], data[3], ts->i2caddr); > + > + mutex_lock(&ts->tr_mutex); > + > + msg.addr = ts->i2caddr; > + msg.flags = ts->client->flags & I2C_M_TEN; > + msg.len = len; > + msg.buf = (char *)data; > + > + rc = i2c_transfer(adap, &msg, 1); > + if (rc != 1) > + dev_err(&ts->client->dev, > + "[ELAN] i2c_transfer write fail, rc=%d\n", rc); > + > + mutex_unlock(&ts->tr_mutex); > + > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > + transmitted, else error code. */ > + return (rc == 1) ? len : rc; > + > +} > + > +/** > + * @brief \b elan_ts_get_data - set command to TP. > + * @param client : our i2c device > + * @param data : data to be received from TP. > + * @param len : command length usually. > + * > + * get data from our TP. > + */ > +static int elants_get_data( > + struct i2c_client *client, > + const u8 *buf, > + size_t len) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + struct i2c_adapter *adap = client->adapter; > + struct i2c_msg msg; > + int rc = 0; > + > + dev_dbg(&client->dev, > + "[ELAN] Enter: %s id:0x%x\n", __func__, ts->i2caddr); > + > + mutex_lock(&ts->tr_mutex); > + > + msg.addr = ts->i2caddr; > + msg.flags = client->flags & I2C_M_TEN; > + msg.flags |= I2C_M_RD; > + msg.len = len; > + msg.buf = (char *)buf; > + > + rc = i2c_transfer(adap, &msg, 1); > + if (rc != 1) > + dev_err(&client->dev, > + "[ELAN] i2c_transfer read fail, rc=%d\n", rc); > + > + mutex_unlock(&ts->tr_mutex); > + > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > + transmitted, else error code. */ > + return (rc == 1) ? len : rc; > +} > + > +/** > + * @brief \b elants_is_iap - is it in iap mode ? > + * @param client : our i2c device > + * @param addr : i2c address > + * > + * check whether fw currently is in iap. > + * @return : > + * < 0 :normal mode > + * > 0 :iap mode or abnormal. > + */ > +static int elants_is_iap( > + struct i2c_client *client, > + u16 addr) > +{ > + int rc; > + uint8_t buf_recv[4] = {0}; > + struct elants_data *ts = i2c_get_clientdata(client); > + > + ts->i2caddr = addr; > + rc = elants_get_data(client, buf_recv, 4); > + if ((rc < 0) || memcmp(buf_recv, iniap_packet, 4)) { > + dev_err(&client->dev, > + "[elan] %s: ID 0x%x can't boot IAP!\n", > + __func__, addr); > + rc = -EINVAL; > + } > + > + ts->i2caddr = elan_i2c_master; > + > + return rc; > +} > + > +static int elants_boot( > + struct i2c_client *client, > + u16 addr, > + elan_boot_t type) > +{ > + int rc; > + struct elants_data *ts = i2c_get_clientdata(client); > + uint8_t command[2][4] = { > + {0x4D, 0x61, 0x69, 0x6E}, /* normal_command */ > + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ > + }; > + > + ts->i2caddr = addr; > + rc = elants_set_data(client, command[(int32_t)type], 4); > + if (rc != 4) { > + if (type == E_BOOT_IAP) > + dev_err(&client->dev, > + "[elan] Boot IAP fail, error=%d\n", > + rc); > + else > + dev_dbg(&client->dev, > + "[elan] Boot normal fail, error=%d\n", > + rc); > + ts->i2caddr = elan_i2c_master; > + return -EINVAL; > + } > + pr_info("[elan] Boot success -- 0x%x\n", addr); > + ts->i2caddr = elan_i2c_master; > + return 0; > +} > + > +static int elants_enter_iap(struct i2c_client *client) > +{ > + int rc = 0, i; > + struct elants_data *ts = i2c_get_clientdata(client); > + > + elan_hw_reset(client); > + > + /* Boot devices to IAP mode */ > + for (i = 0; i < CHIP_NUM; i++) { > + rc = elants_boot(client, tp_device_addr[i], E_BOOT_IAP); > + if (rc < 0) > + return rc; > + } > + > + elan_msleep(10); > + > + /* Check if devices can enter IAP mode */ > + for (i = 0; i < CHIP_NUM; i++) { > + rc = elants_is_iap(client, tp_device_addr[i]); > + if (rc < 0) > + return rc; > + } > + > + ts->iap_mode = IAP_MODE_ENABLE; > + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; > + ts->i2caddr = elan_i2c_master; > + > + return rc; > +} > + > + > +/** > + * @brief \b elan_ts_async_recv_data - get TP status > + * @param client : our i2c device > + * @param cmd : asking command > + * @param buf : result > + * @param size : command length usually. > + * > + * set command type and TP will return its status in buf. > + */ > +static int elants_async_recv_data( > + struct i2c_client *client, > + const uint8_t *cmd, > + uint8_t *buf, size_t tx_size, > + size_t rx_size) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + dev_dbg(&client->dev, > + "[ELAN] Enter: %s\n", __func__); > + > + if (buf == NULL) > + return -EINVAL; > + > + mutex_lock(&ts->tr_mutex); > + if ((i2c_master_send(client, cmd, tx_size)) != tx_size) { > + dev_err(&client->dev, > + "%s: i2c_master_send failed\n", __func__); > + goto fail; > + } > + > + if (unlikely(elan_ts_poll(client) < 0)) > + goto fail; > + else { > + if (i2c_master_recv(client, buf, rx_size) != rx_size) > + goto fail; > + mutex_unlock(&ts->tr_mutex); > + } > + > + return 0; > + > +fail: > + mutex_unlock(&ts->tr_mutex); > + return -EINVAL; > +} > + > +/** > + * @brief \b __get_bc_version - probe /INT to get new/old BC > + * @param client : our i2c device > + * > + * 1 : new bootcode > + * 0 : old bootcode > + */ > +static int __get_bc_version(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + > + /* wait 150ms to probe /INT and get old-BC, new-BC status */ > + msleep(150); > + > + if (gpio_get_value(ts->intr_gpio) == 0) { > + /* Old BC */ > + pr_info("[ELAN]detect intr=>Old FW\n"); > + old_bootcode = true; > + } else { > + /* New BC */ > + pr_info("[elan]detect intr=>New FW\n"); > + old_bootcode = false; > + } > + > + return old_bootcode; > +} > + > + > +/** > + * @brief __hello_packet_handler - hadle hello packet. > + * @param client : our i2c device > + * > + * @return { >0 means success, > + * otherwise fail. } > + * > + * Normal hello packet is {0x55, 0x55, 0x55, 0x55} > + * recovery mode hello packet is {0x55, 0x55, 0x80, 0x80} > + */ > +static int __hello_packet_handler(struct i2c_client *client) > +{ > + int rc = 0, tries = 5; > + uint8_t buf_recv[4] = {0}; > + struct elants_data *ts = i2c_get_clientdata(client); > + > + > +retry: /* wait INT for 1sec */ > + rc = elan_ts_poll(client); > + if (rc < 0) { > + dev_err(&client->dev, > + "%s: poll failed!\n", ELAN_DEV_NAME); > + if (tries-- > 0) > + goto retry; > + } > + > + rc = elants_get_data(client, buf_recv, 4); > + > + elan_dbg(client, > + "[ELAN] rc = %d Hello Packet: [0x%.2x 0x%.2x 0x%.2x 0x%.2x]\n", > + rc, buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]); > + > + if (rc != 4) { > + dev_err(&client->dev, > + "[ELAN] %s: Try recovery because of no hello\n", > + __func__); > + > + /* force TP FW into IAP mode. */ > + elants_enter_iap(client); > + return -EINVAL; > + } > + > + /* new iap main-flow recovery mechanism 20120518*/ > + if (memcmp(buf_recv, hello_packet, 4) || > + elants_is_iap(client, elan_i2c_slave1) > 0) { > + dev_err(&client->dev, > + "[ELAN] got mainflow recovery message\n"); > + elants_enter_iap(client); > + return -EINVAL; > + } > + > + ts->i2caddr = elan_i2c_master; > + > + return rc; > +} > + > +static int __fw_packet_handler(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int rc, tries = 3; > + const uint8_t cmd[] = {CMD_R_PKT, 0x00, 0x00, 0x01}; > + uint8_t buf_recv[4] = {0x0}; > + > + /* Command not support in IAP recovery mode */ > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return 0; > +retry: > + rc = elants_acqurie_data(client, cmd, buf_recv, 4); > + if (rc < 0) > + return rc; > + > + if (buf_recv[0] == CMD_S_PKT) { > + ts->major_fw_version = ((buf_recv[1] & 0x0f) << 4) | > + ((buf_recv[2] & 0xf0) >> 4); > + ts->minor_fw_version = ((buf_recv[2] & 0x0f) << 4) | > + ((buf_recv[3] & 0xf0) >> 4); > + > + /* For 1+2, support this function since Solution version 0.3E(2012/2/23) */ > + if ((ts->major_fw_version == 0x00 && > + ts->minor_fw_version == 0x00) || > + (ts->major_fw_version == 0xFF && > + ts->minor_fw_version == 0xFF)) { > + dev_err(&client->dev, > + "\n\n[ELAN] FW version is empty, suggest IAP ELAN chip\n\n"); > + return -EINVAL; > + } > + > + elan_dbg(client, > + "[ELAN] TOUCH MAJOR FW VERSION 0x%02x\n", > + ts->major_fw_version); > + elan_dbg(client, > + "[ELAN] TOUCH MINOR FW VERSION 0x%02x\n", > + ts->minor_fw_version); > + } else { > + if (tries > 0) { > + tries--; > + goto retry; > + } > + ts->major_fw_version = 0xff; > + ts->minor_fw_version = 0xff; > + dev_err(&client->dev, > + "\n\n[ELAN] FW version is empty, suggest IAP ELAN chip\n\n"); > + return -EINVAL; > + > + } > + > + return 0; > +} > + > +static int __touch_get_resolution(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int rc; > + uint8_t buf_recv[17] = {0x0}; > + const uint8_t get_resolution_cmd[] = { > + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00 > + }; > + > + /* Command not support in IAP recovery mode */ > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return 0; > + > + rc = elants_async_recv_data(client, get_resolution_cmd, buf_recv, > + sizeof(get_resolution_cmd), sizeof(buf_recv)); > + if (rc < 0) > + return -rc; > + > + > + elan_dbg(client, > + "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", > + buf_recv[2], buf_recv[6], buf_recv[10], > + buf_recv[3], buf_recv[7], buf_recv[0], rc); > + > + if (buf_recv[0] != 0x9B) > + return -EINVAL; > + > + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", > + buf_recv[2], buf_recv[6], buf_recv[10], > + buf_recv[3], buf_recv[7], buf_recv[11], rc); > + > + > + ts->rows = (buf_recv[2] + buf_recv[6] + buf_recv[10]); > + ts->cols = (buf_recv[3] + buf_recv[7] + buf_recv[11]); > + > + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x\n", buf_recv[2], > + buf_recv[6], buf_recv[10], > + buf_recv[3], buf_recv[7], buf_recv[11]); > + > + if (ts->rows < 2 || ts->cols < 2) { > + dev_err(&client->dev, > + "[ELAN] Invalid resolution (%d, %d)\n", > + ts->rows, ts->cols); > + > + /* set default resolution if TP information is wrong */ > + ts->rows = ELAN_X_MAX; > + ts->cols = ELAN_Y_MAX; > + > + rc = ret_fail; > + } > + > + /* translate trace number to TSP resolution */ > + ts->cols = ELAN_TS_RESOLUTION(ts->cols); > + ts->rows = ELAN_TS_RESOLUTION(ts->rows); > + > + elan_dbg(client, > + "[ELAN] resolution rows = 0x%02x, cols = 0x%02x\n", > + ts->rows, ts->cols); > + > + return 0; > +} > + > + > +/** > + * elan_touch_get_bc_ver - obtain bootcode data from device > + * @client: the interface we are querying > + > + * > + * Send a bootcode version command and fill the results into the > + * elan device structures. Caller must hold the mutex > + * > + > + * Returns 0 or an error code > + */ > +static int __elan_touch_get_bc_ver(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + const u8 get_bc_ver_cmd[] = {0x53, 0x10, 0x00, 0x01}; > + u8 buf_recv[4]; > + int rc; > + > + /* Command not support in IAP recovery mode */ > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return 0; > + > + rc = elants_acqurie_data(client, get_bc_ver_cmd, > + buf_recv, sizeof(buf_recv)); > + if (rc < 0) { > + dev_err(&client->dev, > + "elan_ts_acqurie_data failed: get_bc_ver\n"); > + return rc; > + } > + > + ts->major_bc_version = (((buf_recv[1] & 0x0f) << 4) | > + ((buf_recv[2]&0xf0) >> 4)); > + ts->minor_bc_version = (((buf_recv[2] & 0x0f) << 4) | > + ((buf_recv[3]&0xf0) >> 4)); > + > + elan_dbg(client, > + "ELAN TOUCH MAJOR BC VERSION 0x%02x\n", > + ts->major_bc_version); > + elan_dbg(client, > + "ELAN TOUCH MINOR BC VERSION 0x%02x\n", > + ts->minor_bc_version); > + > + return 0; > +} > + > +static int __elan_fastboot(struct i2c_client *client, int *count) > +{ > + int rc = 0; > + > + rc = elants_boot(client, elan_i2c_slave1, E_BOOT_NORM); > + if (rc < 0) { > + if (*count > 0) > + return -EAGAIN; > + return -1; > + } > + > + rc = elants_boot(client, elan_i2c_master, E_BOOT_NORM); > + if (rc < 0) { > + if (*count > 0) > + return -EAGAIN; > + return -1; > + } > + > + msleep(100); > + > + return rc; > +} > + > +/** > + * @brief elan_open - open elan device > + * @param input : input device > + * > + */ > +static int elan_open(struct input_dev *input) > +{ > + struct elants_data *ts = input_get_drvdata(input); > + > + dev_err(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + /* wait probe work_func done */ > + while ((ts->status & STA_INIT4) == 0) > + elan_msleep(1); > + > + return 0; > +} > + > +/** > + * @brief elan_close - close input device > + * @param input : input device > + * > + */ > +static void elan_close(struct input_dev *input) > +{ > + struct elants_data *ts = input_get_drvdata(input); > + > + dev_err(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + return; > +} > + > + > +/** > + * @brief \b elan_ts_setup - initialization process. > + * @param client : our i2c client > + * > + * set our TP up > + * -# reset > + * -# hello packet > + * -# fw version > + * -# TP info (resolution) > + * > + */ > +static int __devinit elants_setup(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int rc, tries = 5, count = 5; > + int len = 0; > + bool fastboot = true; > + > +retry: > + /*! Reset */ > + elan_hw_reset(client); > + > + ts->rx_size = queue_header_size; > + > + /* New BootCode */ > + if (fastboot == true) { > + rc = __elan_fastboot(client, &count); > + if (rc < 0) { > + if (rc == -EAGAIN && --count > 0) > + goto retry; > + else { > + len += sprintf(ts->boot_log + len, > + "serious bug, BC may be broken!!\n"); > + return -1; > + } > + } > + fastboot = 0; > + } else { > + /* Wait bootcode timeout 1 second + > + Main-flow initial ~100ms */ > + ssleep(2); > + } > + > + /*! - elan hello packet init */ > + rc = __hello_packet_handler(client); > + if (rc < 0) { > + /* Wrong hello_packet or polling fail, retry */ > + if ((rc == -EINVAL || rc == -ETIMEDOUT) && --tries > 0) { > + dev_err(&client->dev, > + "[ELAN] retries=%d, rc=%d\n", > + tries, rc); > + goto retry; > + } > + > + len += sprintf(ts->boot_log + len, > + "retry=%d polling time-out\n", 5-tries); > + > + dev_err(&client->dev, > + "hello packet error.\n"); > + /* Go through down*/ > + > + /* if iap mode enable, return and wait for IAP */ > + if (ts->iap_mode == IAP_MODE_ENABLE) { > + len += sprintf(ts->boot_log + len, "IAP mode enable\n"); > + return rc; > + } > + } > + > + elan_dbg(client, > + "__hello_packet_handler ...\n"); > + > + /*! - elan fw version */ > + rc = __fw_packet_handler(client); > + if (rc < 0) { > + dev_err(&client->dev, "firmware checking error.\n"); > + len += sprintf(ts->boot_log+len, "fw_packet_error=%x:%x\n", > + ts->major_fw_version, ts->minor_fw_version); > + > + if (rc == -EINVAL) { > + len += sprintf(ts->boot_log+len, > + "FW version is empty, IAP enable!!\n"); > + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; > + ts->iap_mode = IAP_MODE_ENABLE; > + } > + > + /* Go through down*/ > + } > + > + elan_dbg(client, "__fw_packet_handler...\n"); > + > + /*! - elan TP information */ > + rc = __touch_get_resolution(client); > + if (rc < 0) { > + dev_err(&client->dev, > + "TP information checking error.\n"); > + len += sprintf(ts->boot_log+len, > + "touch_get_resolution=%x:%x\n", > + ts->rows, ts->cols); > + /* Go through down*/ > + } > + > + elan_dbg(client, > + "__touch_get_resolution...\n"); > + > + > + /* Get TS BootCode version */ > + rc = __elan_touch_get_bc_ver(client); > + if (rc < 0) { > + dev_err(&client->dev, > + "TP get BC version error.\n"); > + len += sprintf(ts->boot_log+len, > + "touch_get_BC_ver=%x:%x\n", > + ts->major_bc_version, ts->major_bc_version); > + /* Go through down*/ > + } > + > + if (len == 0) > + len += sprintf(ts->boot_log, "boot success!!\n"); > + > + return rc; > + > +} > + > + > +static int elants_get_power_state(struct i2c_client *client) > +{ > + int rc = 0; > + const uint8_t cmd[] = {CMD_R_PKT, 0x50, 0x00, 0x01}; > + uint8_t buf[4], power_state; > + > + rc = elants_acqurie_data(client, cmd, buf, 4); > + if (rc) > + return rc; > + > + power_state = buf[1]; > + elan_dbg(client, > + "dump repsponse: %0x\n", power_state); > + power_state = (power_state & PWR_STATE_MASK) >> 3; > + elan_dbg(client, > + "power state = %s\n", > + power_state == PWR_STATE_DEEP_SLEEP ? > + "Deep Sleep" : "Normal/Idle"); > + > + return power_state; > +} > + > + > +/** > + * @brief \b elan_ts_recv_data - received TP data > + * @param client: our i2c device > + * @param buf : buffer for put received data > + * > + * received data from TP device. > + */ > +static int elants_recv_data(struct i2c_client *client, uint8_t *buf) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int rc = 0, bytes_to_recv; > + > + dev_dbg(&client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + if (buf == NULL) > + return -EINVAL; > + > + mutex_lock(&ts->tr_mutex); > + > + bytes_to_recv = ts->rx_size; > + rc = i2c_master_recv(client, buf, bytes_to_recv); > + > + dev_dbg(&client->dev, > + "[ELAN] %d:%d:%x:%x, addr=%x\n", bytes_to_recv, rc, > + buf[0], buf[34], client->addr); > + dev_dbg(&client->dev, > + "[ELAN] %x:%x:%x:%x:%x:%x\n", buf[0], buf[1], buf[2], > + buf[3], buf[4], buf[5]); > + > + if (rc != bytes_to_recv) { > + dev_err(&client->dev, > + "%s: i2c_master_recv error?!\n", > + __func__); > + rc = -EINVAL; > + } > + mutex_unlock(&ts->tr_mutex); > + > + return rc; > +} > + > +/** > + * elan_touch_pull_frame - pull a frame from the fifo > + * @ed: our elan touch device > + * @ehr: return buffer for the header > + * @buf: data buffer > + * > + * Pulls a frame from the FIFO into the provided ehr and data buffer. > + * The data buffer must be at least cmd_response_len bytes long. > + */ > +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf) > +{ > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + WARN_ON(kfifo_get(ts->fifo, buf, cmd_response_len) > + != cmd_response_len); > +#else > + WARN_ON(kfifo_out_locked(&ts->fifo, buf, cmd_response_len, > + &ts->rx_kfifo_lock) > + != cmd_response_len); > +#endif Target mainline. > + return cmd_response_len; > +} > + > + > +/** > + * elan_touch_fifo_clean_old - Make room for new frames > + * @ed: our elan touch device > + * @room: space needed > + * > + * Empty old frames out of the FIFO until we can fit the new one into > + * the other end. > + */ > +static void elan_touch_fifo_clean_old(struct elants_data *ts, int room) > +{ > + u8 buffer[cmd_response_len]; > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + while (kfifo_len(ts->fifo) + room >= FIFO_SIZE) > + elan_touch_pull_frame(ts, buffer); > +#else > + while (kfifo_len(&ts->fifo) + room >= FIFO_SIZE) > + elan_touch_pull_frame(ts, buffer); > +#endif Mainline. > +} > + > + > +/** @brief \b elan_ts_GetRepoInfo - parse Multi-queue report header > + * @param client : our i2c device > + * @param buf : buffer data > + * > + * parsing report header and get data length. > + * > + */ > +static int elants_GetRepoInfo(struct i2c_client *client, uint8_t *buf) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + struct multi_queue_header *buff = (struct multi_queue_header *)buf; > + const u8 wait_packet[4] = {0x64, 0x64, 0x64, 0x64}; > + int times = 10, rc = 0; > + > + switch (buf[idx_finger_header]) { > + case cmd_header_byte_hello: > + if (!memcmp(buf, hello_packet, 4)) > + ts->wdt_reset++; > + return ret_cmdrsp; > + case cmd_header_byte_response: > + /* Queue the data, using the fifo lock to serialize the multiple > + accesses to the FIFO */ > + elan_dbg(client, > + "[ELAN] recv cmd_header_byte_response\n"); > + > + mutex_lock(&ts->fifo_mutex); > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + if (kfifo_len(ts->fifo) + cmd_response_len >= FIFO_SIZE) > +#else > + if (kfifo_len(&ts->fifo) + cmd_response_len >= FIFO_SIZE) > + > +#endif > + /* Make room, make room */ > + elan_touch_fifo_clean_old(ts, cmd_response_len); > + /* Push the data */ > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > + kfifo_put(ts->fifo, buf, cmd_response_len); > +#else > + kfifo_in_locked(&ts->fifo, buf, > + cmd_response_len, &ts->rx_kfifo_lock); > +#endif Alse mainline. > + mutex_unlock(&ts->fifo_mutex); > + > + elan_dbg(client, > + "[ELAN] wake_up [%02x:%02x:%02x:%02x]\n", > + buf[0], buf[1], buf[2], buf[3]); > + wake_up(&ts->wait); > + return ret_cmdrsp; > + /* Buffer mode header */ > + case queue_header_byte_normal: > + elan_dbg(client, > + "[ELAN] report_count=%d report_len=%d\n", > + buff->report_count, buff->report_length); > + if (likely(buff->report_count <= 3)) { > + ts->mq_header.report_count = buff->report_count; > + ts->mq_header.report_length = buff->report_length; > + ts->rx_size = ts->mq_header.report_length; > + } else > + return ret_fail; > + > + break; > + case queue_header_byte_wait: > + dev_err(&client->dev, > + "========queue_header_byte_wait %x:%x:%x:%x========\n", > + buf[0], buf[1], buf[2], buf[3]); > + /*! BUGFIX: buff[0] might be wrong (0x63), check buff[1 ~ 3] is enough*/ > + if (!memcmp(buf+1, &wait_packet[1], 3)) { > + do { > + udelay(30); > + elants_recv_data(client, (uint8_t *)buff); > + } while (buff->packet_id != queue_header_byte_normal && > + --times > 0); > + if (times > 0) > + rc = elants_GetRepoInfo(client, (uint8_t *)buff); > + else > + return ret_fail; > + elan_dbg(client, > + "Detect Wait_Header:rx_size=%d, report_count=%d \ > + report_len=%d\n", ts->rx_size, > + ts->mq_header.report_count, > + ts->mq_header.report_length); > + > + } else > + dev_err(&client->dev, > + "[ELAN] ERROR!! wait header:%x:%x:%x:%x\n", > + buf[0], buf[1], buf[2], buf[3]); > + break; > + /* Not buffer mode, it's single word mode */ > + case queue_header_byte_Single: > + ts->mq_header.report_count = 1; > + ts->mq_header.report_length = IDX_PACKET_SIZE_WIDTH; > + ts->rx_size = ts->mq_header.report_length; > + return ts->rx_size; > + break; > + default: > + dev_err(&client->dev, > + "unknown multi-queue command!! --%x %x:%x:%x--\n", > + buf[0], buf[1], buf[2], buf[3]); > + ts->mq_header_fail++; > + > + /* If glitch causes frame error, drop all finger report */ > + ts->rx_size = queue_packet_max_len; > + elants_recv_data(client, (uint8_t *)buff); > + return ret_fail; > + } > + > + return ts->rx_size; > +} > + > + > +/** @brief \b elan_touch_checksum - Add for checksum mechanism > + * @param client : our i2c device > + * @param buf : buffer data > + * > + * caculating checksum for make sure all data validity. > + * > + */ > +static int elan_touch_checksum(struct i2c_client *client, u8 *buf) > +{ > + u8 i = 0, checksum = 0; > + struct elants_data *ts = i2c_get_clientdata(client); > + > + /*! FIXME: checksum wasn't including width byte */ > + for (i = 0; i < IDX_PACKET_SIZE_WIDTH - 6; i++) > + checksum = checksum + buf[i]; > + > + ts->checksum_correct = checksum; > + > + if (checksum != buf[idx_finger_width_checksum]) { > + ts->checksum_fail++; > + dev_err(&client->dev, > + "elan touch checksum fail: %02x:%02x\n", > + checksum, buf[idx_finger_width_checksum]); > + return -EFAULT; > + } > + > + return 0; > +} > + > +/** > + * @brief \b elan_touch_parse_fid - parse the 10 fid bits > + * @param data : the input bit stream > + * @param fid : an array of fid values > + * > + * Unpack the 10 bits into an array. > + * > + * FIXME: Review whether we can't just use << operators after making > + * sure the bits are native endian ? > + */ > +static inline void elan_touch_parse_fid(u8 *data, u8 *fid) > +{ > + fid[0] = (data[0] & 0x01); > + fid[1] = (data[0] & 0x02); > + fid[2] = (data[0] & 0x04); > + fid[3] = (data[0] & 0x08); > + fid[4] = (data[0] & 0x10); > + fid[5] = (data[0] & 0x20); > + fid[6] = (data[0] & 0x40); > + fid[7] = (data[0] & 0x80); > + fid[8] = (data[1] & 0x10); > + fid[9] = (data[1] & 0x20); > +} > + > +/** > + * @brief \b elan_touch_parse_wid - parse the 10 wid bits > + * @param data : the input bit stream > + * @param wid : an array of width level > + * > + * Unpack the 10 bits into an array. > + * > + */ > +static inline void elan_touch_parse_wid(u8 *data, u8 *wid) > +{ > + wid[0] = (data[0] >> 4); > + wid[1] = (data[0] & 0x0f); > + wid[2] = (data[1] >> 4); > + wid[3] = (data[1] & 0x0f); > + wid[4] = (data[2] >> 4); > + wid[5] = (data[2] & 0x0f); > + wid[6] = (data[3] >> 4); > + wid[7] = (data[3] & 0x0f); > + wid[8] = (data[4] >> 4); > + wid[9] = (data[4] & 0x0f); > +} > + > + > +static inline int elants_parse_xy( > + uint8_t *data, > + uint16_t *x, > + uint16_t *y) > +{ > + *x = *y = 0; > + > + *x = (data[0] & 0xf0); > + *x <<= 4; > + *x |= data[1]; > + > + *y = (data[0] & 0x0f); > + *y <<= 8; > + *y |= data[2]; > + > + return 0; > +} > + > +/* transition stage for lookup by table, maybe update finger report > + using by Win8 50Byte format that having actual value for X, Y width */ > +static inline int elan_lookup_wid( > + u8 data, > + u16 *w, > + u16 *h) > +{ > + static u16 pre_w, pre_h, cur_w, cur_h; > + const u8 width_lookup_table[15][2] = { > + {3, 2}, {3, 2}, {6, 3}, > + {6, 3}, {10, 3}, {15, 4}, > + {15, 5}, {15, 5}, {15, 5}, > + {15, 5}, {15, 5}, {15, 5}, > + {15, 5}, {15, 5}, {15, 5}, > + }; > + > + cur_w = width_lookup_table[data][0] * 17; > + cur_h = width_lookup_table[data][1] * 17; > + > + /* IIR to balance w, h vaule, don't jitter too much */ > + *w = (cur_w + pre_w) >> 1; > + *h = (cur_h + pre_h) >> 1; > + > + pre_w = cur_w; > + pre_h = cur_h; > + > + return 0; > +} > + > +static int elan_mt_compute_slot(struct mt_device *td) > +{ > + int i; > + for (i = 0; i < td->maxcontacts; ++i) { > + if (td->slots[i].contactid == td->curdata.contactid && > + td->slots[i].touch_state) > + return i; > + } > + for (i = 0; i < td->maxcontacts; ++i) { > + if (!td->slots[i].seen_in_this_frame && > + !td->slots[i].touch_state) > + return i; > + } > + /* should not occurs. If this happens that means > + * that the device sent more touches that it says > + * in the report descriptor. It is ignored then. */ > + return -1; > +} > + > +/* > + * this function is called when a whole contact has been processed, > + * so that it can assign it to a slot and store the data there > + */ > +static void elan_mt_complete_slot(struct mt_device *td) > +{ > + td->curdata.seen_in_this_frame = true; > + if (td->curvalid) { > + int slotnum = elan_mt_compute_slot(td); > + > + if (slotnum >= 0 && slotnum < td->maxcontacts) > + td->slots[slotnum] = td->curdata; > + } > + td->num_received++; > +} > + > +/* > + * this function is called when a whole packet has been received and processed, > + * so that it can decide what to send to the input layer. > + */ > + > +#if (MT_TYPE_B == 0) > +static void elan_mt_emit_event( > + struct mt_device *td, > + struct input_dev *input) > +{ > + struct elants_data *ts = > + container_of(td, struct elants_data, td); > + int i; > + > + for (i = 0; i < td->maxcontacts; ++i) { > + struct mt_slot *s = &(td->slots[i]); > + if (!s->seen_in_this_frame) { > + if (s->touch_state) { > + s->touch_state = false; > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) > + /* ingore finger-up event since TF1 seems having problem */ > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->touch_state); > + input_mt_sync(input); > +#endif > + } > + s->seen_in_this_frame = false; > + continue; > + } > + > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->touch_state); > + > + if (s->touch_state) { > + /* this finger is on the screen */ > + int wide = (s->w > s->h); > + /* divided by two to match visual scale of touch */ > + int major = max(s->w, s->h) >> 1; > + int minor = min(s->w, s->h) >> 1; > + > + elan_dbg(ts->client, > + "[ELAN] i=%d x=%x y=%x w=%x h=%x, p=%x\n", > + i, s->x, s->y, major, minor, s->p); > + > + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, s->contactid); > + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); > + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); > + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) > + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); > +#endif > + input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, major); > + > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) > + /* Can't not report on Android 2.2, 10th finger will disorder */ > + input_event(input, EV_ABS, ABS_MT_WIDTH_MINOR, minor); > +#endif > + } > + input_mt_sync(input); > + s->seen_in_this_frame = false; > + } > + > + input_sync(input); > + td->num_received = 0; > +} > +#else /* For Android ICS, input protocol-B */ > +static void elan_mt_emit_event( > + struct mt_device *td, > + struct input_dev *input) > +{ > + struct elants_data *ts = > + container_of(td, struct elants_data, td); > + int i; > + > + for (i = 0; i < td->maxcontacts; ++i) { > + struct mt_slot *s = &(td->slots[i]); > + if (!s->seen_in_this_frame) > + s->touch_state = false; > + > + input_mt_slot(input, i); > + input_mt_report_slot_state(input, MT_TOOL_FINGER, s->touch_state); > + if (s->touch_state) { > + /* this finger is on the screen */ > + int wide = (s->w > s->h); > + /* divided by two to match visual scale of touch */ > + int major = max(s->w, s->h) >> 1; > + int minor = min(s->w, s->h) >> 1; > + > + elan_dbg(ts->client, > + "[ELAN] i=%d x=%x y=%x w=%x h=%x\n", > + i, s->x, s->y, major, minor); > + > + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); > + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); > + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); > + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); > + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); > + } > + s->seen_in_this_frame = false; > + } > + > + input_mt_report_pointer_emulation(input, true); > + input_sync(input); > + td->num_received = 0; > +} > + > +#endif > + > + > +/** @brief \b elan_mt_event - process finger reports > + * @param ts: our touchscreen > + * @param finger_stat : number of fingers in packet > + * @param buf : received buffer > + * > + * Walk the received report and process the finger data, extracting > + * and reporting co-ordinates. No locking is needed here as the workqueue > + * does our threading for us. > + */ > +static int elan_mt_event( > + struct elants_data *ts, > + int finger_stat, > + u8 *buf) > +{ > + int i; > + u8 fid[FINGER_NUM], wid[FINGER_NUM]; > + u16 x, y, w, h; > + struct i2c_client *client = ts->client; > + struct mt_device *td = &ts->td; > + > + dev_dbg(&client->dev, > + "[ELAN] Enter elan_mt_event Func\n"); > + > + /* Parsing Finger, Width field */ > + elan_touch_parse_fid(&buf[idx_finger_state], &fid[0]); > + elan_touch_parse_wid(&buf[idx_finger_width], &wid[0]); > + > + /* number of fingers expects in this frame */ > + td->num_expected = finger_stat; > + for (i = 0; i < td->maxcontacts; i++) { > + if (finger_stat == 0) > + break; > + > + /* tracking id */ > + td->curdata.contactid = (fid[i] > 0) ? i+1 : 0; > + > + if (td->curdata.contactid == 0) > + continue; > + > + td->curdata.touch_state = true; > + > + elants_parse_xy(&buf[3+i*3], &x, &y); > + td->curdata.x = x; > + td->curdata.y = y; > + > + elan_lookup_wid(wid[i], &w, &h); > + td->curdata.w = w; > + td->curdata.h = h; > + > + finger_stat--; > + > + elan_mt_complete_slot(td); > + } > + > + if (td->num_received >= td->num_expected) > + elan_mt_emit_event(td, ts->input); > + > + return 1; > +} > + > + > + > +/** @brief \b elants_report_data - report finger report to user space. > + * @param client : our i2c device > + * @param buf : raw data from TP device. > + * > + * - reporting finger data to user space. > + * - packet_fail count > + * > + */ > +static void elants_report_data(struct i2c_client *client, uint8_t *buf) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + > + switch (buf[idx_finger_header]) { > + case idx_coordinate_packet_4_finger: > + case idx_coordinate_packet_10_finger: { > + u8 finger_stat = buf[idx_finger_total] & 0x0f; > + elan_dbg(client, > + "[ELAN] finger_stat == %d\n", finger_stat); > + elan_dbg(client, > + "[ELAN] finger:%x:%x:%x:%x:%x\n", > + buf[0], buf[1], buf[2], buf[3], buf[4]); > + > + /* Enter right process, reset int_status*/ > + ts->polling.int_status = 0; > + ts->packet_received++; > + > + if (likely(finger_stat != 0)) { > + ts->td.curvalid = true; > + /* Old way for report input event > + elan_touch_report_fingers(ts, finger_stat, buf); */ > + elan_mt_event(ts, finger_stat, buf); > + ts->touched_sync++; > + } else { > + input_mt_sync(ts->input); > + ts->no_touched_sync++; > + input_sync(ts->input); > + } > + } > + break; > + default: > + ts->header_fail++; > + dev_err(&client->dev, > + "[ELAN] %s: unknown packet type: %x:%x:%x:%x\n", > + __func__, buf[0], buf[1], buf[2], buf[3]); > + break; > + } > + > + return; > +} > + > +/** @brief \b elants_drop_packet - err handler that drop all packet. > + * > + * mutex protection will at outside this function. > + * > + */ > +static void elants_drop_packet(struct elants_data *ts) > +{ > + uint8_t buf[queue_packet_max_len] = {0x0}; > + > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return; > + > + if (gpio_get_value(ts->intr_gpio) != 0) > + goto end; > + > + /* -# Read multi_queue header */ > + ts->rx_size = queue_header_size; > + if (elants_recv_data(ts->client, buf) < 0) > + goto end; > + > + /* -# Get multi_queue header info*/ > + if (elants_GetRepoInfo(ts->client, buf) > 0) { > + /* 3. Get finger report data */ > + elants_recv_data(ts->client, buf); > + udelay(10); > + if (gpio_get_value(ts->intr_gpio) != 0) > + goto end; > + } > + > + /* -#received all packet till ts->intr pull high */ > + ts->rx_size = 1; > + while (gpio_get_value(ts->intr_gpio) == 0) { > + elants_recv_data(ts->client, buf); > + udelay(10); > + } > + > +end: > + ts->rx_size = queue_header_size; > + dev_dbg(&ts->client->dev, > + "[ELAN] Leave %s\n", __func__); > + > + return; > +} > + > + > + > +/** @brief \b elan_ts_pollWork_func - regular processing > + * @param work : pass from kernel > + * > + * poll_timer polled processing to occur elan_ts_pollWork_func() to check if our ts > + * is abnormal. > + * > + */ > +static void elants_pollWork_func(struct work_struct *work) > + > +{ > + struct elants_data *ts = > + container_of(work, struct elants_data, pollingwork); > + > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + goto fail; > + > + if (gpio_get_value(ts->intr_gpio) != 0) > + goto fail; > + else { > + ts->polling.int_status++; > + if (ts->polling.int_status < 3) > + goto fail; > + } > + > + /* - we use mutex_trylock() here since polling is not very important */ > + if (!mutex_trylock(&ts->mutex)) { > + elan_dbg(ts->client, > + "[ELAN] trylock fail! return...\n"); > + goto fail; > + } > + > + if (test_bit(idx_finger_report, &ts->busy)) { > + elan_dbg(ts->client, > + "[ELAN] 1.finger_Report processing ... ignore!!\n"); > + goto unlock; > + } > + > + if (test_bit(idx_cmd_handshake, &ts->busy)) { > + elan_dbg(ts->client, > + "[ELAN] 2.command processing ... ignore!!\n"); > + goto unlock; > + } > + > + dev_err(&ts->client->dev, > + "[ELAN] Force to release intr_gpio!!\n"); > + > + ts->drop_frame++; > + elants_drop_packet(ts); > + > +unlock: > + mutex_unlock(&ts->mutex); > + > +fail: > + return; > +} > + > +static irqreturn_t elants_work_func(int irq, void *work) > +{ > + struct elants_data *ts = work; > + uint8_t buf[queue_packet_max_len] = {0x0}; > + u8 pos = 0, rc; > + > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return IRQ_HANDLED; > + > + /* - this means that we have already serviced it or glich happen!! */ > + if (gpio_get_value(ts->intr_gpio) != 0) { > + dev_dbg(&ts->client->dev, > + "[ELAN] Leave %s(gpio)\n", __func__); > + return IRQ_HANDLED; > + } > + > + mutex_lock(&ts->mutex); > + > + set_bit(idx_finger_report, &ts->busy); > + > + /* Report one setting */ > + /* ts->rx_size = 40; */ /* queue_header_size; */ > + /* ts->mq_header.report_count=1; */ > + > + > + /* - Read multi_queue header */ > + if (elants_recv_data(ts->client, buf) < 0) > + goto fail; > + > + /* - Get multi_queue header info */ > + rc = elants_GetRepoInfo(ts->client, buf); > + if (rc < 0 || rc == ret_cmdrsp) > + goto fail; > + > + /* - Get finger report data */ > + if (elants_recv_data(ts->client, buf) < 0) > + goto fail; > + > + clear_bit(idx_finger_report, &ts->busy); > + > + mutex_unlock(&ts->mutex); > + > + /* - parsing report and send out */ > + while (ts->mq_header.report_count--) { > + if (elan_touch_checksum(ts->client, buf + pos) == 0) > + elants_report_data(ts->client, buf + pos); > + pos = pos + IDX_PACKET_SIZE_WIDTH; > + udelay(10); > + } > + > + ts->rx_size = queue_header_size; > + dev_dbg(&ts->client->dev, > + "[ELAN] Leave %s\n", __func__); > + return IRQ_HANDLED; > + > +fail: > + clear_bit(idx_finger_report, &ts->busy); > + mutex_unlock(&ts->mutex); > + ts->rx_size = queue_header_size; > + > + dev_dbg(&ts->client->dev, > + "[ELAN] Leave %s(Fail)\n", __func__); > + return IRQ_HANDLED; > +} > + > + > + > +/** > + * @brief \b elan_touch_timer_func - poll processing > + * @param timer : our timer > + * > + * Queue polled processing to occur on our touch panel and kick the timer > + * off again > + * > + * CHECK: we have no guarantee that the timer will not run multiple times > + * within one poll, does it matter ? > + */ > +static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer) > +{ > + struct elants_data *ts = container_of(timer, struct elants_data, timer); > + queue_work(ts->elan_wq, &ts->work); > + hrtimer_start(&ts->timer, > + ktime_set(0, 12500000), HRTIMER_MODE_REL); > + > + return HRTIMER_NORESTART; > +} > + > +/** > + * @brief \b elants_poll_timer_func - err handler when intr_gpio is low but no isr serviced it. > + * @param data : our ts > + * > + * intr_gpio polling checking, it'll force to get data if intr_gpio is low > + * and not in isr routine. > + * > + */ > +static void elants_poll_timer_func(unsigned long data) > +{ > + struct elants_data *ts = (struct elants_data *)data; > + struct elan_polling *timer = &ts->polling; > + > + dev_dbg(&ts->client->dev, > + "[ELAN] Enter %s\n", __func__); > + > + /* - ignore it if normal work is processing */ > + if (work_pending(&ts->work)) { > + dev_dbg(&ts->client->dev, > + "[ELAN] 1.work_pending ... ignore!!\n"); > + goto reset; > + } > + > + /* - ignore it if poll_timer work is processing */ > + if (work_pending(&ts->pollingwork)) { > + dev_dbg(&ts->client->dev, > + "[ELAN] 2.work_pending ... ignore!!\n"); > + goto reset; > + } > + > + if (!queue_work(timer->elan_wq, &ts->pollingwork)) > + dev_err(&ts->client->dev, > + "[ELAN] pollWork active failed!!\n"); > + > +reset: > + timer->timer.expires = jiffies + 10 * HZ; > + add_timer(&ts->polling.timer); > + > + return; > +} > + > +static irqreturn_t elants_irq_handler(int irq, void *dev_id) > +{ > + struct elants_data *ts = dev_id; > + struct i2c_client *client = ts->client; > + > + dev_dbg(&client->dev, > + "[ELAN] %s\n", __func__); > + > + ts->irq_received++; > + > + return IRQ_WAKE_THREAD; > +} > + > + > +static int elants_register_interrupt(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + int err = 0; > + > + if (client->irq) { > + > + ts->use_irq = 1; > + > + err = request_threaded_irq(client->irq, elants_irq_handler, > + elants_work_func, > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, > + client->name, ts); > + if (err) > + dev_err(&client->dev, > + "%s: request_irq %d failed\n", > + __func__, client->irq); > + > + ts->status |= STA_USE_IRQ; > + > + elan_dbg(client, > + "[ELAN] %s in interrupt mode\n", ts->input->name); > + } > + > + if (!ts->use_irq) { > + hrtimer_init(&ts->timer, > + CLOCK_MONOTONIC, HRTIMER_MODE_REL); > + ts->timer.function = elan_touch_timer_func; > + hrtimer_start(&ts->timer, ktime_set(1, 0), > + HRTIMER_MODE_REL); > + elan_dbg(client, > + "[ELAN] %s in time-polling mode\n", ts->input->name); > + } > + > + return err; > +} > + > +static int remove_elants(struct i2c_client *client) > +{ > + int ret = 0; > + struct elants_data *ts = i2c_get_clientdata(client); > + > + if (&client->dev.kobj) > + sysfs_remove_group(&client->dev.kobj, &elan_attribute_group); > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > + if (&ts->early_suspend) > + unregister_early_suspend(&ts->early_suspend); > +#endif > + > + if (ts->use_irq) { > + if (client->irq) > + free_irq(client->irq, ts); > + } else { > + if (&ts->timer) > + hrtimer_cancel(&ts->timer); > + } > + > +#if (MT_TYPE_B) > + input_mt_destroy_slots(ts->input); > +#endif > + > + if (ts->input) > + input_unregister_device(ts->input); > + > + if (ts->elan_wq) > + destroy_workqueue(ts->elan_wq); > + > + if (ts->polling.elan_wq) > + destroy_workqueue(ts->polling.elan_wq); > + > + if (&ts->polling.timer) { > + ret = del_timer(&ts->polling.timer); > + if (ret != 0) > + dev_err(&client->dev, > + "[ELAN] del_timer fail!!\n"); > + } > + > + if (&ts->mutex) > + mutex_destroy(&ts->mutex); > + if (&ts->tr_mutex) > + mutex_destroy(&ts->tr_mutex); > + if (&ts->fifo_mutex) > + mutex_destroy(&ts->fifo_mutex); > + > + if (ts->fw_enabled) { > + ret = misc_deregister(&ts->firmware); > + if (ret < 0) > + dev_err(&client->dev, > + "[ELAN] misc_deregister fail!!\n"); > + } > + > + kfree(ts->td.slots); > + > + /* free kfifo */ > + kfifo_free(&ts->fifo); > +#if (ANDROID) > + wake_lock_destroy(&ts->wakelock); > +#endif > + > + kfree(ts); > + > + return ret; > +} > + > + > +/** > + * probe_thread_func - init touch device. > + * @work: /int work queue > + * > + * Perform real probe for our I2C device and if successful configure > + * it up as an input device. If not then clean up and return an error > + * code. > + */ > + > +static int probe_thread_func(void *data) > +{ > + struct elants_data *ts = data; > + struct i2c_client *client = ts->client; > + int err = 0; > + > + mutex_lock(&ts->mutex); > + > + err = elants_setup(client); > + if (err < 0) { > + dev_err(&client->dev, > + "[ELAN] %s probe failed\n", > + ELAN_DEV_NAME); > + /* User can IAP anyway, continue */ > + } > + > + set_bit(BTN_TOUCH, ts->input->keybit); > + > + /*! - Single touch input params setup */ > + input_set_abs_params(ts->input, ABS_X, 0, ts->cols, 0, 0); > + input_set_abs_params(ts->input, ABS_Y, 0, ts->rows, 0, 0); > + input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0); > + > + /*! @warning FIXME: ABS_MT_PRESSURE can be supported by FW. > + currently we have done it by ABS_MT_WIDTH_MAJOR */ > + /*! - Multitouch input params setup */ > + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->cols, 0, 0); > + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->rows, 0, 0); > +#if (MT_TYPE_B == 0) > + input_set_abs_params(ts->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); > + input_set_abs_params(ts->input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0); > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 1, 0, 0); > + > + input_set_abs_params(ts->input, ABS_MT_TRACKING_ID, 0, FINGER_NUM, 0, 0); > +#else > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); > + > + /* not test, keep mark for less Android-ICS */ > + input_set_abs_params(ts->input, ABS_MT_ORIENTATION, 0, 1, 0, 0); > +#endif > + > + __set_bit(EV_ABS, ts->input->evbit); > + __set_bit(EV_SYN, ts->input->evbit); > + > + input_set_drvdata(ts->input, ts); > + > + err = input_register_device(ts->input); > + if (err) { > + dev_err(&client->dev, > + "[ELAN] %s unable to register input device\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + ts->td.slots = kzalloc(FINGER_NUM * sizeof(struct mt_slot), > + GFP_KERNEL); > + if (!ts->td.slots) { > + dev_err(&client->dev, > + "cannot allocate multitouch slots\n"); > + goto fail_un; > + } > + ts->td.maxcontacts = FINGER_NUM; > +#if (MT_TYPE_B) > + input_mt_init_slots(ts->input, ts->td.maxcontacts); > +#endif > + > + /*! @warning If the firmware device fails we carry on as it doesn't stop normal > + usage */ > + private_ts = &ts->firmware; > + if (elants_register_interrupt(ts->client) < 0) { > + dev_err(&client->dev, > + "[ELAN] %s register_interrupt failed!\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + /*! - Register SW watchdog timer */ > + init_timer(&ts->polling.timer); > + /*! - check intr_gpio every 10secs */ > + ts->polling.timer.expires = jiffies + 10 * HZ; > + ts->polling.timer.function = &elants_poll_timer_func; > + ts->polling.timer.data = (unsigned long)ts; > + add_timer(&ts->polling.timer); > + > + mutex_unlock(&ts->mutex); > + > + ts->status |= STA_INIT4; > + > + return 0; > + > +fail_un: > + mutex_unlock(&ts->mutex); > + remove_elants(client); > + return err; > +} > + > + > + > +/** > + * @brief elants_probe - probe for touchpad > + * @param client : our I2C client > + * > + * Perform setup and probe for our I2C device and if successful configure > + * it up as an input device. If not then clean up and return an error > + * code. > + */ > +static int __devinit elants_probe( > + struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + long err = -1; > + struct elan_i2c_platform_data *pdata = NULL; > + struct elants_data *ts = > + kzalloc(sizeof(struct elants_data), GFP_KERNEL); > + > + if (ts == NULL) > + return -ENOMEM; > + > + ts->status |= STA_PROBED; > + > + mutex_init(&ts->mutex); > + mutex_init(&ts->tr_mutex); > + mutex_init(&ts->fifo_mutex); > + init_waitqueue_head(&ts->wait); > + spin_lock_init(&ts->rx_kfifo_lock); > +#if (ANDROID) > + wake_lock_init(&ts->wakelock, WAKE_LOCK_SUSPEND, "elan_touch"); > +#endif > + > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > + dev_err(&client->dev, > + "[ELAN] %s: i2c check functionality error\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + /* @{ - Normal operatin (Finger report) */ > + ts->elan_wq = create_singlethread_workqueue("elan_wq"); > + if (!ts->elan_wq) { > + dev_err(&client->dev, > + "[ELAN] %s: create workqueue failed\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + /*! - Regular polling Process */ > + ts->polling.elan_wq = create_singlethread_workqueue("elan_poll_wq"); > + if (!ts->polling.elan_wq) { > + dev_err(&client->dev, > + "[ELAN] %s: create workqueue failed\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + /* INIT polling machinams */ > + INIT_WORK(&ts->pollingwork, elants_pollWork_func); > + > + pdata = client->dev.platform_data; > + if (!pdata) { > + dev_err(&client->dev, > + "[ELAN] %s no platform data\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + ts->status &= STA_INIT; > + > + ts->intr_gpio = pdata->intr_gpio; > + ts->rst_gpio = pdata->rst_gpio; > + /* set initial i2c address */ > + client->addr = elan_i2c_master; > + ts->i2caddr = client->addr; > + > + elan_dbg(client, > + "reset=%d, intr=%d\n", ts->rst_gpio, ts->intr_gpio); > + > + ts->client = client; > + i2c_set_clientdata(client, ts); > + > + /* INIT kfifo */ > + err = kfifo_alloc(&ts->fifo, FIFO_SIZE, GFP_KERNEL); > + if (!kfifo_initialized(&ts->fifo)) { > + dev_err(&client->dev, > + "%s error kfifo_alloc\n", __func__); > + goto fail_un; > + } > + > + > + /*! - IAP Device Initial */ > + ts->firmware.minor = MISC_DYNAMIC_MINOR; > + ts->firmware.name = "elan-iap"; > + ts->firmware.fops = &elan_touch_fops; > + ts->firmware.mode = S_IRWXUGO; > + > + if (unlikely(misc_register(&ts->firmware) < 0)) > + dev_err(&client->dev, > + "[ELAN] IAP device register failed!!"); > + else { > + elan_dbg(client, > + "[ELAN] IAP device register finished!!"); > + ts->fw_enabled = 1; > + } > + > + ts->status &= STA_INIT2; > + > + ts->input = input_allocate_device(); > + if (ts->input == NULL) { > + dev_err(&client->dev, > + "[ELAN] %s Failed to allocate input device\n", > + ELAN_DEV_NAME); > + goto fail_un; > + } > + > + ts->input->name = ELAN_DEV_NAME; > + ts->input->open = elan_open; > + ts->input->close = elan_close; > + > + /* Says HELLO to touch device */ > + ts->thread = kthread_run(probe_thread_func, ts, client->name); > + if (IS_ERR(ts->thread)) { > + err = PTR_ERR(ts->thread); > + goto fail_un; > + } > + > + ts->status &= STA_INIT3; > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING - 1; > + ts->early_suspend.suspend = elants_early_suspend; > + ts->early_suspend.resume = elants_late_resume; > + register_early_suspend(&ts->early_suspend); > +#endif > + > + /* - register sysfs @} */ > + elan_dbg(client, > + "[ELAN] create sysfs!!\n"); > + if (sysfs_create_group(&client->dev.kobj, &elan_attribute_group)) > + dev_err(&client->dev, > + "[ELAN] sysfs create group error\n"); > + > + > + return 0; > + > +fail_un: > + remove_elants(client); > + return err; > +} > + > +static int __devexit elants_remove(struct i2c_client *client) > +{ > + return remove_elants(client); > +} > + > +#ifdef CONFIG_PM_SLEEP > +/** @brief \b elan_ts_suspend - enter sleep mode > + * > + * when Suspend mode, disable_irq and cancel work queue. > + * > + */ > +static int elants_suspend(struct i2c_client *client, pm_message_t mesg) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + const u8 set_sleep_cmd[] = {0x54, 0x50, 0x00, 0x01}; > + int rc = 0; > + > + dev_err(&client->dev, > + "[ELAN] %s: enter\n", __func__); > + > + /* Command not support in IAP recovery mode */ > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return 0; > + > + disable_irq(client->irq); > + cancel_work_sync(&ts->work); > + > + mutex_lock(&ts->mutex); > + rc = elants_set_data(client, > + set_sleep_cmd, sizeof(set_sleep_cmd)); > + > + if (rc < 0) > + goto end; > + > + ts->power_state = 0; > + /* de-active timer */ > + del_timer_sync(&ts->polling.timer); > + > + > +end: > + mutex_unlock(&ts->mutex); > + return rc; > +} > + > + > +/** @brief \b elan_ts_resume - enter wake-up mode > + * > + * when Wake-up mode, enable_irq. > + * > + */ > +static int elants_resume(struct i2c_client *client) > +{ > + struct elants_data *ts = i2c_get_clientdata(client); > + const u8 set_active_cmd[] = {0x54, 0x58, 0x00, 0x01}; > + int rc = 0, retry = 5; > + > + dev_err(&client->dev, > + "[ELAN] %s: enter\n", > + __func__); > + > + /* Command not support in IAP recovery mode */ > + if (ts->protocol & PRO_UPDATE_FW_MODE) > + return 0; > + > + mutex_lock(&ts->mutex); > + > + do { > + rc = elants_set_data(client, > + set_active_cmd, sizeof(set_active_cmd)); > + elan_msleep(2); > + rc = elants_get_power_state(client); > + if (unlikely(rc != PWR_STATE_NORMAL)) > + dev_err(&client->dev, > + "[ELAN] %s: wake up tp failed! err = %d\n", > + __func__, rc); > + else > + break; > + } while (--retry); > + > + ts->power_state = 1; > + mutex_unlock(&ts->mutex); > + > + /* re-active poll timer */ > + mod_timer(&ts->polling.timer, jiffies + 10 * HZ); > + > + enable_irq(client->irq); > + > + return rc; > +} > +#endif > + > + > +#ifdef CONFIG_HAS_EARLYSUSPEND Convert to mainline PM functions. Use CONFIG_PM_SLEEP and SIMPLE_DEV_PM_OPS > +/** @brief \b elants_early_suspend - enter sleep mode > + * > + * This function is called by kernel, > + * elan_ts_early_suspend() -> elan_ts_suspend() > + * > + */ > +static void elants_early_suspend(struct early_suspend *h) > +{ > + struct elants_data *ts = > + container_of(h, struct elants_data, early_suspend); > + dev_dbg(&ts->client->dev, > + "Enter %s\n", __func__); > + > + elants_suspend(ts->client, PMSG_SUSPEND); > + return; > +} > + > +/** @brief \b elants_late_resume - enter wake-up mode > + * > + * This function is called by kernel, > + * elan_ts_late_resume() -> elan_ts_resume() > + * > + */ > +static void elants_late_resume(struct early_suspend *h) > +{ > + struct elants_data *ts = > + container_of(h, struct elants_data, early_suspend); > + dev_dbg(&ts->client->dev, > + "Enter %s\n", __func__); > + > + elants_resume(ts->client); > + return; > +} > +#endif > + > +/*! brief system registeration */ > +static const struct i2c_device_id elan_ts_id[] = { > + { ELAN_DEV_NAME, 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, elan_ts_id); > + > +/*! brief system registeration */ > +static struct i2c_driver elan_ts_driver = { > + .probe = elants_probe, > + .remove = __devexit_p(elants_remove), > +#ifndef CONFIG_HAS_EARLYSUSPEND > + .suspend = elants_suspend, > + .resume = elants_resume, > +#endif > + .id_table = elan_ts_id, > + .driver = { > + .name = ELAN_DEV_NAME, > + .owner = THIS_MODULE, > + .bus = &i2c_bus_type, > + }, > +}; > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) > +static int __devinit elan_ts_init(void) > +{ > + return i2c_add_driver(&elan_ts_driver); > +} > + > +static void __exit elan_ts_exit(void) > +{ > + i2c_del_driver(&elan_ts_driver); > + return; > +} > +module_init(elan_ts_init); > +module_exit(elan_ts_exit); > +#else > +module_i2c_driver(elan_ts_driver); Target mainline and use this only. > +#endif > + > +MODULE_VERSION(DRIVER_VERSION); > +MODULE_DESCRIPTION("Elan TouchScreen Driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/i2c/elants.h b/include/linux/i2c/elants.h > new file mode 100644 > index 0000000..5b45a0c > --- /dev/null > +++ b/include/linux/i2c/elants.h > @@ -0,0 +1,61 @@ > +#ifndef _LINUX_I2C_ELANTS_H > +#define _LINUX_I2C_ELANTS_H > + > +#define ELAN_DEV_NAME "elants_i2c" > + > +/* set TSP resolution by actual TPM */ > +#define ELAN_X_MAX 2944 > +#define ELAN_Y_MAX 1856 > + > + > +/*! @brief \b platform data > + * @param intr_gpio : gpio pin > + * @param rst_gpio : interuption pin > + * > + * Platform data, all platform dependent variable should put here. > + * > + */ > +struct elan_i2c_platform_data { > + unsigned short version; > + unsigned int abs_x_min; > + unsigned int abs_x_max; > + unsigned int abs_y_min; > + unsigned int abs_y_max; > + int intr_gpio; > + int rst_gpio; > +}; > + > +struct elan_ioctl_data { > + int len; > + char buf[32]; > +}; > + > +/*! @brief \b ioctl command definition. > + * @param IOCTL_I2C_SLAVE : set i2c address that would be controled right now. > + * @param IOCTL_MAJOR_FW_VER : fw major number > + * @param IOCTL_MINOR_FW_VER : fw minor number > + * @param IOCTL_RESET : Hardware Reset > + * @param IOCTL_SET_COMMAND : control command set to TP device. > + * @param IOCTL_GET_COMMAND : get response from our TP device. > + * @param IOCTL_IAP_ENABLE : Enter IAP mode > + * @param IOCTL_IAP_DISABLE : Leave IAP mode > + * @param IOCTL_I2C_INT : gpio status > + * > + * > + * ioctl command, easy user to control our TP from application. > + * > + */ > +#define ELAN_IOCTLID 0xD0 > +#define IOCTL_I2C_SLAVE _IOW(ELAN_IOCTLID, 1, int) > +#define IOCTL_MAJOR_FW_VER _IOR(ELAN_IOCTLID, 2, int) > +#define IOCTL_MINOR_FW_VER _IOR(ELAN_IOCTLID, 3, int) > +#define IOCTL_RESET _IOW(ELAN_IOCTLID, 4, int) > +#define IOCTL_I2C_INT _IOR(ELAN_IOCTLID, 8, int) > +#define IOCTL_SET_COMMAND _IOW(ELAN_IOCTLID, 9, unsigned long) > +#define IOCTL_GET_COMMAND _IOR(ELAN_IOCTLID, 10, unsigned long) > +#define IOCTL_IAP_ENABLE _IOW(ELAN_IOCTLID, 11, int) > +#define IOCTL_IAP_DISABLE _IOW(ELAN_IOCTLID, 12, int) > +#define IOCTL_CHECK_RECOVERY_MODE _IOR(ELAN_IOCTLID, 13, int) > +#define IOCTL_BOOTCODE_CMD _IOW(ELAN_IOCTLID, 14, unsigned long) > + > +#endif /* _LINUX_I2C_ELANTS_H */ > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Oct 22, 2012 at 11:47:42AM +0800, Jian-Jhong Ding wrote: > Scott Liu <scott.liu@emc.com.tw> writes: > > + > > +struct mt_device { > > + struct mt_slot curdata; /* placeholder of incoming data */ > > + __u8 num_received; /* how many contacts we received */ > > + __u8 num_expected; /* expected last contact index */ > > + __u8 maxcontacts; > > + bool curvalid; /* is the current contact valid? */ > > + struct mt_slot *slots; > > +}; > > With Benjamin's i2c-hid implimentation, is it possible to make > hid-multitouch not depend on USBHID and reuse it to drive this device? Exactly. Before looking any further - is this the same part that Tom Lin posted a driver for earlier this summer? Thanks.
On Mon, Oct 22, 2012 at 6:07 PM, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote: > On Mon, Oct 22, 2012 at 11:47:42AM +0800, Jian-Jhong Ding wrote: >> Scott Liu <scott.liu@emc.com.tw> writes: >> > + >> > +struct mt_device { >> > + struct mt_slot curdata; /* placeholder of incoming data */ >> > + __u8 num_received; /* how many contacts we received */ >> > + __u8 num_expected; /* expected last contact index */ >> > + __u8 maxcontacts; >> > + bool curvalid; /* is the current contact valid? */ >> > + struct mt_slot *slots; >> > +}; >> >> With Benjamin's i2c-hid implimentation, is it possible to make >> hid-multitouch not depend on USBHID and reuse it to drive this device? We can already use hid-multitouch with hid over I2C devices (I'm testing my i2c devices with this module). But it's true that hid-multitouch depends on usbhid, and I the funny think is that I was removing this dependency today. The fact is that currently, i2c devices do not segfault with hid-multitouch because win8 devices do not require anymore to set some feature at plug. > > Exactly. Before looking any further - is this the same part that Tom Lin > posted a driver for earlier this summer? I'm not Elan, and I can not be sure, but judging from the "hello packets" and the other commands, I doubt this device is an I2C over HID one. Anyway, reusing hid-multitouch for this specific case seams to be a little bit difficult. You would have first to provide a fake report descriptor (or inject commands as if you were hid-core) and then, you would have to reformat the incoming data into valid win7 (or 8) packets. Judging by the functions elan_touch_parse_fid, elan_touch_parse_wid and elants_parse_xy, the data seem to be mixed (all the finger ids at the beginning, and not one per touch), so it is definitively needed to recreate a valid HID packet. So I'm not surprised with the duplicated code. However, Henrik did a big job in kernel 3.7 to factorize mt code, and some part of the duplication can be achieve with these functions (look at input_mt_sync_frame and input_mt_get_slot_by_key for example). Cheers, Benjamin > > Thanks. > > -- > Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Dmitry Torokhov <dmitry.torokhov@gmail.com> writes: > On Mon, Oct 22, 2012 at 11:47:42AM +0800, Jian-Jhong Ding wrote: >> With Benjamin's i2c-hid implimentation, is it possible to make >> hid-multitouch not depend on USBHID and reuse it to drive this device? > > Exactly. Before looking any further - is this the same part that Tom Lin > posted a driver for earlier this summer? No. Tom's driver was only for trackpads, this is probably for some touch panel. The controller ICs may have some similarities, but the firmware is developed independently, though both somewhat conform to the HID over I2C protocol. Thanks, -JJ > Thanks. > > -- > Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi JJ. Most of your comment I'll follow and I just thought it can be guarded by #if CONFIG_xxx, then it can be easy compiled under x86 without error. But OK, I'll remove those mess code. > -----Original Message----- > From: linux-input-owner@vger.kernel.org [mailto:linux-input-owner@vger.kernel.org] On > Behalf Of Jian-Jhong Ding > Sent: Monday, October 22, 2012 11:48 AM > To: Scott Liu; Dmitry Torokhov; linux-input@vger.kernel.org; linux-i2c@vger.kernel.org; > linux-kernel@vger.kernel.org > Cc: Benjamin Tissoires; Jesse; Vincent Wang; Paul > Subject: Re: [PATCH v1] Support Elan Touchscreen eKTF product. > > Hi Scott, > > Some comments and questions below, > > Thanks, > -JJ > Scott Liu <scott.liu@emc.com.tw> writes: > > This patch is for Elan eKTF Touchscreen product, I2C adpater module. > > > > This driver adds communication support with Elan eKTF controller using I2C bus. > > > > Signed-off-by: Scott Liu <scott.liu@emc.com.tw> > > --- > > drivers/input/touchscreen/Kconfig | 9 + > > drivers/input/touchscreen/Makefile | 1 + > > drivers/input/touchscreen/elants_i2c.c | 3182 > ++++++++++++++++++++++++++++++++ > > include/linux/i2c/elants.h | 61 + > > 4 files changed, 3253 insertions(+) > > create mode 100644 drivers/input/touchscreen/elants_i2c.c > > create mode 100644 include/linux/i2c/elants.h > > > > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig > > index 1ba232c..50e6f05 100644 > > --- a/drivers/input/touchscreen/Kconfig > > +++ b/drivers/input/touchscreen/Kconfig > > @@ -237,6 +237,15 @@ config TOUCHSCREEN_EETI > > To compile this driver as a module, choose M here: the > > module will be called eeti_ts. > > > > +config TOUCHSCREEN_ELAN > > + tristate "Elan touchscreen panel support" > > + depends on I2C > > + help > > + Say Y here to enable support for I2C connected Elan touch panels. > > + > > + To compile this driver as a module, choose M here: the > > + module will be called elants_i2c. > > + > > config TOUCHSCREEN_EGALAX > > tristate "EETI eGalax multi-touch panel support" > > depends on I2C > > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile > > index 178eb12..428a631 100644 > > --- a/drivers/input/touchscreen/Makefile > > +++ b/drivers/input/touchscreen/Makefile > > @@ -28,6 +28,7 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += > edt-ft5x06.o > > obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o > > obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o > > obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o > > +obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o > > obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o > > obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o > > obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o > > diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c > > new file mode 100644 > > index 0000000..d87b2ef > > --- /dev/null > > +++ b/drivers/input/touchscreen/elants_i2c.c > > @@ -0,0 +1,3182 @@ > > +/* > > + * Elan Microelectronics touchpanels with I2C interface > > + * > > + * Copyright (C) 2012 Elan Microelectronics Corporation. > > + * Scott Liu <scott.liu@emc.com.tw> > > + * > > + * This code is partly based on hid-multitouch.c: > > + * > > + * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> > > + * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> > > + * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France > > + * > > + */ > > + > > +/* > > + * This software is licensed under the terms of the GNU General Public > > + * License version 2, as published by the Free Software Foundation, and > > + * may be copied, distributed, and modified under those terms. > > + * > > + */ > > + > > +#define CHIP_NUM 2 > > What does this constant mean? What chip does this refer to? We have different solution for different size of panels and consist of number of chips. So I think that is it possible to define this constant here and edit by the real solution? > > + > > +/* Reserved for having platform to test */ > > +#define MT_TYPE_B 0 > > Can't we just go MT-B all the way? Reserved to test what? OK. BTW, it's reserved for early Android upstream. > > +/* Define it if using on Android system */ > > +#define ANDROID 0 > > Please just target mainline. Or at least make this a Kconfig option and > add a second patch for Android. OK, I will target the mainline. > > +#include <linux/module.h> > > +#include <linux/input.h> > > +#include <linux/interrupt.h> > > + > > +#include <linux/platform_device.h> > > +#include <linux/i2c.h> > > +#include <linux/delay.h> > > +#include <linux/gpio.h> > > +#include <linux/jiffies.h> > > +#include <linux/hrtimer.h> > > +#include <linux/timer.h> > > +#include <linux/kthread.h> > > +#include <linux/miscdevice.h> > > +#include <linux/uaccess.h> > > +#include <linux/buffer_head.h> > > +#include <linux/version.h> > > +#include <linux/kfifo.h> > > +#include <linux/slab.h> > > +#if (ANDROID) > > Please drop this. OK > > +#include <linux/wakelock.h> > > +#include <linux/earlysuspend.h> > > +#endif > > +#if (MT_TYPE_B) > > I definitely think we should always use MT-B. > > +#include <linux/input/mt.h> > > +#endif > > + > > +/* debug option */ > > +static bool debug = false; > > +module_param(debug, bool, 0444); > > +MODULE_PARM_DESC(debug, "print a lot of debug information"); > > + > > +#define elan_dbg(client, fmt, arg...) \ > > + if (debug) \ > > + dev_printk(KERN_DEBUG, &client->dev, fmt, ##arg) > > + > > +/*================================================= > > + * Marco > > + *================================================= */ > > +#define DRV_NAME "elants_i2c" > > + > > +#define DRV_MA_VER 2 > > +#define DRV_MI_VER 0 > > +#define DRV_SUB_MI_VER 0 > > + > > +#define _str(s) #s > > +#define str(s) _str(s) > > +#define DRIVER_VERSION > str(DRV_MA_VER.DRV_MI_VER.DRV_SUB_MI_VER) > > + > > + > > +#define IDX_PACKET_SIZE_4FIG 17 > > +#define IDX_PACKET_SIZE_WIDTH 40 > > + > > +#define FINGER_NUM 10 > > + > > +#define PWR_STATE_DEEP_SLEEP 0 > > +#define PWR_STATE_NORMAL 1 > > +#define PWR_STATE_MASK BIT(3) > > + > > +#define CMD_S_PKT 0x52 > > +#define CMD_R_PKT 0x53 > > +#define CMD_W_PKT 0x54 > > + > > +#define HELLO_PKT 0x55 > > +#define NORMAL_PKT 0x62 > > + > > +#define RESET_PKT 0x77 > > +#define CALIB_PKT 0xA8 > > + > > +#define FINGER_ID 1 > > + > > +#define FIFO_SIZE (64) > > + > > + > > +/*! Convert from rows or columns into resolution */ > > +#define ELAN_TS_RESOLUTION(n) ((n - 1) * 64) > > + > > +static const char hello_packet[4] = { 0x55, 0x55, 0x55, 0x55 }; > > +static const char iniap_packet[4] = { 0x55, 0xaa, 0x33, 0xcc }; > > +static const char recov_packet[4] = { 0x55, 0x55, 0x80, 0x80 }; > > + > > +#include <linux/i2c/elants.h> > > Move this up with the rest of the includes. OK > > +/*! Firmware protocol status flag */ > > +#define PRO_I2C_WRT_CMD_SYNC 0x00000001 > > +#define PRO_HID_MOD_CHECKSUM 0x00000002 > > +#define PRO_UPDATE_FW_MODE 0x00000080 > > + > > + > > +/*! driver status flag, should move to public header file */ > > +#define STA_NONINIT 0x00000001 > > +#define STA_INIT 0x00000002 > > +#define STA_INIT2 0x00000004 > > +#define STA_INIT3 0x00000100 > > +#define STA_INIT4 0x00000200 > > +#define STA_PROBED 0x00000008 > > +#define STA_ERR_HELLO_PKT 0x00000010 > > +#define STA_USE_IRQ 0x00000020 > > +#define STA_SLEEP_MODE 0x00000040 > > + > > +/*======================================= > > + * Structure Definition > > + *=======================================*/ > > + > > +/*! @enum elan i2c address definition */ > > +enum elan_i2c_addr { > > + elan_i2c_master = 0x10, > > + elan_i2c_slave1 = 0x20, > > + elan_i2c_slave2 = 0x21, > > + > > + elan_i2c_maxnum = 3, > > +}; > > + > > +/*! @enum finger_report_header Finger report header byte definition */ > > +enum finger_report_header { > > + idx_coordinate_packet_4_finger = 0x5c, > > + idx_coordinate_packet_10_finger = 0x62, > > +}; > > + > > +/*! @enum fw_queue_report_hdr FW Queue report header definition */ > > +enum fw_queue_report_hdr { > > + queue_header_byte_Single = 0x62, > > + queue_header_byte_normal = 0x63, > > + queue_header_byte_wait = 0x64, > > + queue_header_size = 4, > > + queue_packet_max_len = queue_header_size + (IDX_PACKET_SIZE_WIDTH * 3), > > +}; > > + > > +/*! @enum fw_normal_cmd_hdr FW Normal command header definition */ > > +enum fw_normal_cmd_hdr { > > + cmd_header_byte_write = 0x54, > > + cmd_header_byte_read = 0x53, > > + cmd_header_byte_response = 0x52, > > + cmd_header_byte_hello = 0x55, > > + cmd_response_len = 4 > > +}; > > + > > +/*! @enum fw_info_pos FW information position */ > > +enum fw_info_pos { > > + idx_finger_header = 0, > > + idx_finger_state = 1, > > + idx_finger_total = 2, > > + idx_finger_checksum = 34, > > + idx_finger_width = 35, > > + idx_4finger_checksum = 17, > > + idx_finger_width_checksum = 34, > > +}; > > Seems odd to define these as an enum. Define constants? mmm...I consider it is as our firmware contents information so I just make it as enum and easy to manage it. May I keep it? > > +/*! @enum lock_bit Lock bit definition */ > > +enum lock_bit { > > + idx_file_operate = 0, > > + idx_cmd_handshake = 1, > > + idx_finger_report = 2, > > + idx_test_mode = 3, > > +}; > > + > > +enum kfifo_ret { > > + ret_ok = 0, > > + ret_cmdrsp = 1, > > + ret_fail = -1, > > +}; > > + > > +typedef enum elan_boot_e { > > + E_BOOT_NORM = 0, > > + E_BOOT_IAP = 1 > > +} elan_boot_t; > > + > > +/* FW read command, 0x53 0x?? 0x0, 0x01 */ > > +enum { > > + E_ELAN_INFO_FW_VER = 0x00, > > + E_ELAN_INFO_BC_VER = 0x10, > > + E_ELAN_INFO_FW_ID = 0xf0 > > +}; > > + > > + > > +/*! @struct <multi_queue_header> */ > > +struct multi_queue_header { > > + u8 packet_id; > > + u8 report_count; > > + u8 report_length; > > + u8 reserved; > > +}; > > + > > +/*! @brief elan_polling */ > > +struct elan_polling { > > + struct workqueue_struct *elan_wq; /* polling work queue */ > > + struct timer_list timer; /* Polling intr_gpio timer */ > > + u8 int_status; /* polling intr gpio status */ > > +}; > > + > > + > > +/* finger handler, refer to hid-multitouch.c */ > > +struct mt_slot { > > + __s32 x, y, p, w, h; > > + __s32 contactid; /* the device ContactID assigned to this slot */ > > + bool touch_state; /* is the touch valid? */ > > + bool seen_in_this_frame;/* has this slot been updated */ > > +}; > > + > > +struct mt_device { > > + struct mt_slot curdata; /* placeholder of incoming data */ > > + __u8 num_received; /* how many contacts we received */ > > + __u8 num_expected; /* expected last contact index */ > > + __u8 maxcontacts; > > + bool curvalid; /* is the current contact valid? */ > > + struct mt_slot *slots; > > +}; > > With Benjamin's i2c-hid implimentation, is it possible to make > hid-multitouch not depend on USBHID and reuse it to drive this device? I'll reply it at another mail since I saw Dmitry's reply this morning. > > + > > +/** > > +* > > +* @brief elants_data > > +* > > +* all variable should include in this struct. > > +* > > +*/ > > +struct elants_data { > > + int intr_gpio; /* interupter pin*/ > > + int rst_gpio; /* reset pin*/ > > + int use_irq; > > + u8 major_fw_version; > > + u8 minor_fw_version; > > + u8 major_bc_version; > > + u8 minor_bc_version; > > + u8 major_hw_id; > > + u8 minor_hw_id; > > + bool fw_enabled; /* True if firmware device enabled*/ > > + int rows; /* Panel geometry for input layer*/ > > + int cols; > > + int x_max; > > + int y_max; > > + /* Power state 0:sleep 1:active*/ > > + u8 power_state; > > + /* TS is in IAP mode already*/ > > +#define IAP_MODE_ENABLE 1 > > Why not add this as a Kconfig entry? This constant define is to iap_mode variable. iap_mode == IAP_MODE_ENABLE means driver detects firmware fail OR user intends to update firmware by ioctl command. So I think it is not necessary to add this to Kconfig. Thanks, Scott > > + /* 1 : Firmware update mode > > + 0 : normal*/ > > + unsigned int iap_mode; > > + unsigned int rx_size; /* Read size in use*/ > > + > > + /* Multi-queue info */ > > + struct multi_queue_header mq_header; > > + > > + /* our i2c client*/ > > + struct i2c_client *client; > > + /* input device*/ > > + struct input_dev *input; > > + /* normal function work thread*/ > > + struct workqueue_struct *elan_wq; > > + /* normal function work queue*/ > > + struct work_struct work; > > + /* start probe start*/ > > + struct task_struct *thread; > > + /* char device for ioctl and IAP*/ > > + struct miscdevice firmware; > > + /* use timer if use_irq == 0*/ > > + struct hrtimer timer; > > + /* regular polling work thread*/ > > + struct work_struct pollingwork; > > + /* regular polling work queue*/ > > + struct elan_polling polling; > > +#if (ANDROID) > > + struct early_suspend early_suspend; > > + /* avoid sleep during IAP.*/ > > + struct wake_lock wakelock; > > +#endif > > Please drop this. > > + /* Protects I2C accesses to device*/ > > + struct mutex mutex; > > + /* Protects I2C tx/rx*/ > > + struct mutex tr_mutex; > > + > > + /* Lock openers*/ > > + unsigned long busy; > > + /* Protocol stats for firmware*/ > > + unsigned int protocol; > > + > > + /* elan-iap i2c address*/ > > + unsigned short i2caddr; > > + > > + /* fifo and processing */ > > + struct kfifo fifo; > > + > > + /* Serialize operations around FIFO */ > > + struct mutex fifo_mutex; > > + wait_queue_head_t wait; > > + spinlock_t rx_kfifo_lock; > > + > > + /* boot log */ > > + u8 boot_log[256]; > > + > > + /*! Add for TS driver debug */ > > + unsigned int status; > > + long int irq_received; > > + long int packet_received; > > + long int packet_fail; > > + long int touched_sync; > > + long int no_touched_sync; > > + long int checksum_correct; > > + long int wdt_reset; > > + u16 checksum_fail; > > + u16 header_fail; > > + u16 mq_header_fail; > > + u16 drop_frame; > > + > > + /* mt device */ > > + struct mt_device td; > > +}; > > + > > + > > + > > +/*/============================================ > > + * Function prototype > > + *=============================================*/ > > +static int elants_set_data(struct i2c_client *client, > > + const u8 *data, > > + size_t len); > > +static int elants_async_recv_data(struct i2c_client *client, > > + const uint8_t *cmd, > > + uint8_t *buf, > > + size_t tx_size, > > + size_t rx_size); > > + > > +static void elants_drop_packet(struct elants_data *ts); > > +static irqreturn_t elants_work_func(int irq, void *work); > > + > > +static irqreturn_t elants_irq_handler(int irq, void *dev_id); > > +static int elan_hw_reset(struct i2c_client *client); > > +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf); > > + > > +#ifdef CONFIG_HAS_EARLYSUSPEND > > +static void elants_early_suspend(struct early_suspend *h); > > +static void elants_late_resume(struct early_suspend *h); > > +#endif > > Android specific. Please drop. > > + > > +/*================================================= > > + * Global variable > > + *=================================================*/ > > + > > +/*! for elan-iap char driver */ > > +static struct miscdevice *private_ts; > > Firmware updating functionality? What about try to use sysfs entry? > And it would be easier to review if you split firmware updating code > into another patch. > > +static bool old_bootcode = false; > > + > > +static u8 tp_device_addr[3] = {elan_i2c_master, > > + elan_i2c_slave1, > > + elan_i2c_slave2 > > + }; > > + > > + > > +#define elants_acqurie_data(a, cmd, buf, c) \ > > + elants_async_recv_data(a, cmd, buf, c, c) > > + > > +/*================================================= > > + * Function implement > > + *=================================================*/ > > +static inline void elan_msleep(u32 t) > > +{ > > + /* > > + * If the sleeping time is 10us - 20ms, usleep_range() is recommended. > > + * Read Documentation/timers/timers-howto.txt > > + */ > > + usleep_range(t*1000, t*1000 + 500); > > +} > > + > > +/** > > + * @brief __issue_bootcode_cmd - hadle bootcode status. > > + * @param client : our i2c device > > + * > > + * @return { >0 means success, > > + * otherwise fail. } > > + * > > + * Enter Normal mode packet is {0x55, 0x55, 0x55, 0x55} > > + * 2012/3/1 won't issue bootcmd in normal driver initial flow. > > + */ > > +static int elan_bootcode_cmd(struct i2c_client *client, > > + u8 passcode) > > +{ > > + int rc = 0; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + u8 command[2][4] = { > > + {0x45, 0x49, 0x41, 0xFF}, /* normal_command */ > > + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ > > + }; > > + > > + /* Detect old / new FW */ > > + elan_msleep(90); > > + > > + if (gpio_get_value(ts->intr_gpio) == 0) { > > + /* Old FW */ > > + pr_warn("detect intr!!!!! OLD BC detect\n"); > > + return -2; > > + } > > + > > + if (passcode > E_BOOT_IAP) > > + return -1; > > + > > + elan_dbg(client, "passcode = %x:%x:%x:%x\n", > > + command[passcode][0], > > + command[passcode][1], > > + command[passcode][2], > > + command[passcode][3]); > > + > > + mutex_lock(&ts->mutex); > > + > > + ts->i2caddr = elan_i2c_slave2; > > + rc = elants_set_data(client, command[passcode], 4); > > + if (rc < 0) > > + dev_err(&ts->client->dev, > > + "[ELAN] set passcode[%d] fail\n", passcode); > > + > > + ts->i2caddr = elan_i2c_slave1; > > + rc = elants_set_data(client, command[passcode], 4); > > + if (rc < 0) > > + dev_err(&ts->client->dev, > > + "[ELAN] set passcode[%d] fail\n", passcode); > > + > > + ts->i2caddr = elan_i2c_master; > > + rc = elants_set_data(client, command[passcode], 4); > > + if (rc < 0) > > + dev_err(&ts->client->dev, > > + "[ELAN] set passcode[%d] fail\n", passcode); > > + > > + /* normal communitcat interactives with Master. */ > > + ts->i2caddr = elan_i2c_master; > > + mutex_unlock(&ts->mutex); > > + > > + return rc; > > +} > > + > > +/** > > +* @brief \b elan_iap_open - char device > > +* > > +* purpose for ioctl and iap updating fw. > > +*/ > > +int elan_iap_open(struct inode *inode, struct file *filp) > > +{ > > + struct elants_data *ts ; > > + > > + filp->private_data = private_ts; > > + ts = container_of(filp->private_data, > > + struct elants_data, firmware); > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + return 0; > > +} > > + > > +/** > > +* @brief \b elan_iap_release - char device > > +* > > +* purpose for close this device > > +*/ > > +int elan_iap_release(struct inode *inode, struct file *filp) > > +{ > > + struct elants_data *ts = > > + container_of(filp->private_data, > > + struct elants_data, firmware); > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + return 0; > > +} > > + > > +/** > > +* @brief \b elan_iap_write - iap write page data > > +* @param buff is page data from user space > > +* @param count is number of data in byte. > > +* > > +* purpose for iap write page data > > +*/ > > +ssize_t elan_iap_write(struct file *filp, > > + const char *buff, > > + size_t count, > > + loff_t *offp) > > +{ > > + struct elants_data *ts = > > + container_of(filp->private_data, > > + struct elants_data, firmware); > > + int ret; > > + u8 txbuf[256]; > > + struct i2c_adapter *adap = ts->client->adapter; > > + struct i2c_msg msg; > > + > > + if (count > 256) > > + return -EMSGSIZE; > > + > > + if (copy_from_user(txbuf, buff, count)) > > + return -EFAULT; > > + > > + msg.addr = ts->i2caddr; > > + msg.flags = ts->client->flags & I2C_M_TEN; > > + msg.len = count; > > + msg.buf = (char *)txbuf; > > + > > + ret = i2c_transfer(adap, &msg, 1); > > + if (ret != 1) > > + dev_err(&ts->client->dev, > > + "[ELAN] i2c_master_send fail, ret=%d\n", ret); > > + > > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > > + transmitted, else error code. */ > > + return (ret == 1) ? count : ret; > > +} > > + > > +/** > > +* @brief \b elan_iap_read - read status code from TP > > +* > > +* purpose for iap read status code from TP > > +*/ > > +ssize_t elan_iap_read(struct file *filp, > > + char *buff, > > + size_t count, > > + loff_t *offp) > > +{ > > + struct elants_data *ts = > > + container_of(filp->private_data, > > + struct elants_data, firmware); > > + u8 rxbuf[256]; > > + int ret; > > + struct i2c_adapter *adap = ts->client->adapter; > > + struct i2c_msg msg; > > + > > + if (count > 256) > > + return -EMSGSIZE; > > + > > + msg.addr = ts->i2caddr; > > + msg.flags = ts->client->flags & I2C_M_TEN; > > + msg.flags |= I2C_M_RD; > > + msg.len = count; > > + msg.buf = rxbuf; > > + > > + ret = i2c_transfer(adap, &msg, 1); > > + if (ret == 1) > > + if (copy_to_user(buff, rxbuf, count)) > > + return -EFAULT; > > + > > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > > + transmitted, else error code. */ > > + return (ret == 1) ? count : ret; > > +} > > + > > +/** > > +* @brief \b elan_iap_ioctl - ioctl > > +* @param cmd to control our TP device. > > +* @param arg is parameter from user space > > +* > > +* purpose is that control our TP by char device node. > > +*/ > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) > > +static int elan_iap_ioctl(struct inode *inode, > > + struct file *filp, > > + unsigned int cmd, > > + unsigned long arg) > > +#else > > +static long elan_iap_ioctl(struct file *filp, > > + unsigned int cmd, > > + unsigned long arg) > > +#endif > > +{ > > + struct elants_data *ts = > > + container_of(filp->private_data, > > + struct elants_data, firmware); > > + int __user *argp = (int __user *)arg; > > + u8 len, buf[32]; > > + int rc; > > + > > + switch (cmd) { > > + case IOCTL_I2C_SLAVE: > > + ts->i2caddr = (unsigned short)arg; > > + break; > > + case IOCTL_MAJOR_FW_VER: > > + put_user(ts->major_fw_version, argp); > > + break; > > + case IOCTL_MINOR_FW_VER: > > + put_user(ts->minor_fw_version, argp); > > + break; > > + case IOCTL_CHECK_RECOVERY_MODE: > > + put_user(ts->iap_mode, argp); > > + break; > > + case IOCTL_IAP_ENABLE: { > > + disable_irq(ts->client->irq); > > +#if (ANDROID) > > + wake_lock(&ts->wakelock); > > +#endif > > + ts->protocol |= PRO_UPDATE_FW_MODE; > > + } > > + break; > > + case IOCTL_IAP_DISABLE: > > + ts->protocol &= ~PRO_UPDATE_FW_MODE; > > +#if (ANDROID) > > + wake_unlock(&ts->wakelock); > > +#endif > > + enable_irq(ts->client->irq); > > + break; > > + case IOCTL_BOOTCODE_CMD: { > > + int mode; > > + > > + len = 1; > > + if (copy_from_user(buf, (const void *)arg, len)) > > + return -EFAULT; > > + > > + mode = buf[0]; > > + rc = elan_bootcode_cmd(ts->client, (u8)mode); > > + if (rc < 0) { > > + elan_dbg(ts->client, > > + "elan_bootcode_command error\n"); > > + return -1; > > + } > > + } > > + break; > > + case IOCTL_RESET: > > + pr_warn("[ELAN] IOCTL_RESET\n"); > > + rc = elan_hw_reset(ts->client); > > + if (rc < 0) { > > + elan_dbg(ts->client, > > + "[ELAN] elan_hw_reset error\n"); > > + return -1; > > + } > > + break; > > + case IOCTL_I2C_INT: > > + put_user(gpio_get_value(ts->intr_gpio), argp); > > + break; > > + case IOCTL_SET_COMMAND: { > > + struct i2c_client *client = ts->client; > > + struct elan_ioctl_data idata; > > + > > + if (copy_from_user(&idata.len, > > + (const void *)arg, > > + sizeof(idata.len))) { > > + dev_err(&client->dev, > > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + return -EFAULT; > > + } > > + > > + if (copy_from_user(&idata, > > + (const void *)arg, > > + idata.len + sizeof(int))) { > > + dev_err(&client->dev, > > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + return -EFAULT; > > + } > > + dev_dbg(&ts->client->dev, > > + "IOCTL_SET_COMMAND:%x:%x:%x:%x\n", > > + idata.buf[0], > > + idata.buf[1], > > + idata.buf[2], > > + idata.buf[3]); > > + > > + if (idata.buf[0] != CMD_R_PKT && > > + idata.buf[0] != CMD_W_PKT) > > + return -EFAULT; > > + > > + mutex_lock(&ts->mutex); > > + elants_set_data(ts->client, > > + (const u8 *)idata.buf, > > + idata.len); > > + mutex_unlock(&ts->mutex); > > + } > > + break; > > + case IOCTL_GET_COMMAND: { > > + int rc = 0; > > + struct i2c_client *client = ts->client; > > + struct elan_ioctl_data idata; > > + > > + /* Get command length first */ > > + if (copy_from_user(&idata.len, > > + (const void *)arg, > > + sizeof(idata.len))) { > > + dev_err(&client->dev, > > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + return -EFAULT; > > + } > > + > > + /* Get command real length then */ > > + if (copy_from_user(&idata, (const void *)arg, > > + idata.len + sizeof(int))) { > > + dev_err(&client->dev, > > + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + return -EFAULT; > > + } > > + > > + if (idata.buf[0] != CMD_R_PKT && idata.buf[0] != CMD_W_PKT) > > + return -EFAULT; > > + > > + pr_info("[ELAN] IOCTL_GET_COMMAND %x:%x:%x:%x\n", > > + idata.buf[0], idata.buf[1], idata.buf[2], idata.buf[3]); > > + > > + mutex_lock(&ts->mutex); > > + > > + set_bit(idx_cmd_handshake, &ts->busy); > > + > > + elants_set_data(client, idata.buf, idata.len); > > + mutex_unlock(&ts->mutex); > > + > > + /* We will wait for non O_NONBLOCK handles until a signal or data */ > > + mutex_lock(&ts->fifo_mutex); > > + > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + while (kfifo_len(ts->fifo) == 0) { > > +#else > > + while (kfifo_len(&ts->fifo) == 0) { > > +#endif > > + mutex_unlock(&ts->fifo_mutex); > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + rc = wait_event_interruptible_timeout( > > + ts->wait, kfifo_len(ts->fifo), > > + msecs_to_jiffies(3000)); > > +#else > > + rc = wait_event_interruptible_timeout( > > + ts->wait, kfifo_len(&ts->fifo), > > + msecs_to_jiffies(3000)); > > +#endif > > Please drop these CPP version magic. Target only current mainline kernel. > > + if (rc <= 0) { > > + rc = -ETIMEDOUT; > > + dev_err(&client->dev, > > + "timeout!! wake_up(ts->wait) \n"); > > + goto err2; > > + } > > + mutex_lock(&ts->fifo_mutex); > > + } > > + if (elan_touch_pull_frame(ts, idata.buf) < 0) { > > + rc = -1; > > + goto err1; > > + } > > + > > +err1: > > + mutex_unlock(&ts->fifo_mutex); > > +err2: > > + clear_bit(idx_cmd_handshake, &ts->busy); > > + > > + dev_dbg(&client->dev, > > + "[ELAN] Leave %s\n", __func__); > > + > > + if (rc < 0) { > > + rc = copy_to_user((void __user *)arg, > > + (void *)&idata, > > + 4+sizeof(len)); > > + return -rc; > > + } > > + > > + elan_dbg(client, > > + "Reponse=%x:%x:%x:%x\n", > > + idata.buf[0], > > + idata.buf[1], > > + idata.buf[2], > > + idata.buf[3]); > > + > > + if (copy_to_user((void __user *)arg, > > + (void *)&idata, > > + sizeof(idata))) > > + return -EFAULT; > > + > > + } > > + break; > > + default: > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static const struct file_operations elan_touch_fops = { > > + .owner = THIS_MODULE, > > + .open = elan_iap_open, > > + .write = elan_iap_write, > > + .read = elan_iap_read, > > + .release = elan_iap_release, > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) > > + .ioctl = elan_iap_ioctl, > > +#else > > + .unlocked_ioctl = elan_iap_ioctl, > > +#endif > > +}; > > Please drop this. Use sysfs and target mainline. > > +/** > > + * @brief interfaces > > + * provide the hardware and firmware information > > + */ > > +static ssize_t show_fw_version_value( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + return sprintf(buf, "%.2x %.2x\n", > > + ts->major_fw_version, > > + ts->minor_fw_version); > > +} > > + > > + > > +static ssize_t show_bc_version_value( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + return sprintf(buf, "%.2x %.2x\n", > > + ts->major_bc_version, > > + ts->minor_bc_version); > > +} > > + > > +static ssize_t show_drvver_value( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + return sprintf(buf, "%s\n", DRIVER_VERSION); > > +} > > + > > + > > +static ssize_t show_intr_gpio( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + int ret = 0; > > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + ret = gpio_get_value(ts->intr_gpio); > > + > > + return sprintf(buf, "%d\n", ret); > > +} > > + > > +static ssize_t show_adapter_pkt_rvd( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + return sprintf(buf, "irq_received=%ld packet_received=%ld packet_fail=%ld \ > > + mq_hdr_fail=%d, checksum_fail=%x header_fail=%d, \ > > + poll_timer=%d, wdt_reset=%ld\n", > > + ts->irq_received, ts->packet_received, > > + ts->packet_fail, ts->mq_header_fail, > > + ts->checksum_fail, ts->header_fail, > > + ts->drop_frame, ts->wdt_reset); > > +} > > + > > +static ssize_t show_queue_count( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + return sprintf(buf, "queue_report_count=%d, report_length=%d\n", > > + ts->mq_header.report_count, ts->mq_header.report_length); > > +} > > + > > + > > +static ssize_t store_power_mode( > > + struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + int ret = 0; > > + unsigned long val; > > + const char AcMode[] = {0x54, 0x30, 0x00, 0x01}; > > + const char BtMode[] = {0x54, 0x38, 0x00, 0x01}; > > + char cmd[4]; > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + ret = kstrtoul(buf, 10, &val); > > + if (ret) > > + return ret; > > + > > + switch (val) { > > + case 0: > > + memcpy(cmd, AcMode, sizeof(AcMode)); > > + break; > > + case 1: > > + memcpy(cmd, BtMode, sizeof(BtMode)); > > + break; > > + default: > > + return -1; > > + } > > + > > + mutex_lock(&ts->mutex); > > + elants_set_data(ts->client, cmd, sizeof(cmd)); > > + mutex_unlock(&ts->mutex); > > + > > + return count; > > +} > > + > > +static ssize_t show_power_mode( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + const char RespCmd[] = {0x53, 0x30, 0x00, 0x01}; > > + char tbuf[4]; > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return -1; > > + > > + > > + mutex_lock(&ts->mutex); > > + set_bit(idx_cmd_handshake, &ts->busy); > > + elants_acqurie_data(ts->client, RespCmd, tbuf, sizeof(RespCmd)); > > + if (tbuf[0] != cmd_header_byte_response) { > > + dev_err(&client->dev, > > + "exception!! %x:%x:%x:%x\n", > > + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); > > + elants_drop_packet(ts); > > + memset(tbuf, 0x0, sizeof(tbuf)); > > + } > > + clear_bit(idx_cmd_handshake, &ts->busy); > > + mutex_unlock(&ts->mutex); > > + > > + return sprintf(buf, "%x:%x:%x:%x\n", > > + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); > > +} > > + > > +static ssize_t show_report_rate( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + long int old_frame, new_frame; > > + > > + old_frame = ts->packet_received; > > + ssleep(1); > > + new_frame = ts->packet_received; > > + > > + return sprintf(buf, "%ld\n", new_frame - old_frame); > > +} > > + > > +static ssize_t show_iap_mode( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = > > + container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + return sprintf(buf, "%s\n", > > + (ts->iap_mode == 0) ? "Normal" : "Recovery"); > > +} > > + > > +static ssize_t show_recal( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + u8 w_flashkey[4] = {0x54, 0xC0, 0xE1, 0x5A}; > > + u8 rek[4] = {0x54, 0x29, 0x00, 0x01}; > > + > > + ts->i2caddr = elan_i2c_master; > > + elants_set_data(client, w_flashkey, 4); > > + elan_msleep(1); > > + elants_set_data(client, rek, 4); > > + > > + return sprintf(buf, "%s\n", "re-K finish"); > > +} > > + > > + > > +static ssize_t show_boot_log( > > + struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + /* show boot status, allow user cat this log during iap mode enable!! */ > > + > > + return scnprintf(buf, PAGE_SIZE, ts->boot_log); > > +} > > + > > + > > +static DEVICE_ATTR(power_mode, S_IRUGO|S_IWUGO, > > + show_power_mode, store_power_mode); > > +static DEVICE_ATTR(queue_count, S_IRUGO, show_queue_count, NULL); > > +static DEVICE_ATTR(drv_version, S_IRUGO, show_drvver_value, NULL); > > +static DEVICE_ATTR(fw_version, S_IRUGO, show_fw_version_value, NULL); > > +static DEVICE_ATTR(bc_version, S_IRUGO, show_bc_version_value, NULL); > > +static DEVICE_ATTR(ts_packet, S_IRUGO, show_adapter_pkt_rvd, NULL); > > +static DEVICE_ATTR(gpio, S_IRUGO, show_intr_gpio, NULL); > > +static DEVICE_ATTR(report_rate, S_IRUGO, show_report_rate, NULL); > > +static DEVICE_ATTR(boot_log, S_IRUGO, show_boot_log, NULL); > > +static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL); > > +static DEVICE_ATTR(rek, S_IRUGO, show_recal, NULL); > > + > > + > > + > > +static struct attribute *elan_attributes[] = { > > + &dev_attr_power_mode.attr, > > + &dev_attr_queue_count.attr, > > + &dev_attr_fw_version.attr, > > + &dev_attr_drv_version.attr, > > + &dev_attr_bc_version.attr, > > + &dev_attr_gpio.attr, > > + &dev_attr_ts_packet.attr, > > + &dev_attr_report_rate.attr, > > + &dev_attr_boot_log.attr, > > + &dev_attr_iap_mode.attr, > > + &dev_attr_rek.attr, > > + > > + NULL > > +}; > > + > > + > > +static struct attribute_group elan_attribute_group = { > > + .name = "elants", > > + .attrs = elan_attributes, > > +}; > > + > > + > > +/** > > + * @brief elan_hw_reset - h/w reset. > > + * @param client : our i2c device > > + * > > + * @retval >0 means reset success, \n > > + * otherwise is fail. > > + * > > + */ > > +static int elan_hw_reset(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int ret; > > + > > + if (ts->rst_gpio < 0) > > + return -1; > > + > > + ret = gpio_direction_output(ts->rst_gpio, 0); > > + if (ret < 0) { > > + pr_err("gpio_direction fail!\n"); > > + return ret; > > + } > > + > > + elan_msleep(2); > > + > > + ret = gpio_direction_output(ts->rst_gpio, 1); > > + if (ret < 0) > > + return ret; > > + > > + /* wait > 10ms to ensure that fw is in IAP mode */ > > + msleep(20); > > + > > + return ret; > > +} > > + > > + > > + > > +/** > > + * @brief elan_ts_poll - polling intr pin status. > > + * @param client : our i2c device > > + * > > + * @retval 0 means intr pin is low, \n > > + * otherwise is high. > > + * > > + * polling intr pin untill low and the maximus wait time is \b 200ms. > > + */ > > +static int elan_ts_poll(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int status = 0, retry = 20; > > + > > + do { > > + status = gpio_get_value(ts->intr_gpio); > > + elan_dbg(client, > > + "%s: status = %d\n", __func__, status); > > + retry--; > > + mdelay(10); > > + } while (status != 0 && retry > 0); > > + > > + elan_dbg(client, > > + "%s: poll interrupt status %s\n", > > + __func__, status == 1 ? "high" : "low"); > > + return (status == 0 ? 0 : -ETIMEDOUT); > > +} > > + > > +/** > > + * @brief \b elan_ts_set_data - set command to TP. > > + * @param client : our i2c device > > + * @param data : command or data which will send to TP. > > + * @param len : command length usually. > > + * > > + * set command to our TP. > > + */ > > +static int elants_set_data(struct i2c_client *client, > > + const u8 *data, size_t len) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + struct i2c_adapter *adap = client->adapter; > > + struct i2c_msg msg; > > + int rc = 0; > > + > > + dev_dbg(&client->dev, "[ELAN] Enter: %s\n", __func__); > > + elan_dbg(client, > > + "dump cmd: %02x, %02x, %02x, %02x, addr=%x\n", > > + data[0], data[1], data[2], data[3], ts->i2caddr); > > + > > + mutex_lock(&ts->tr_mutex); > > + > > + msg.addr = ts->i2caddr; > > + msg.flags = ts->client->flags & I2C_M_TEN; > > + msg.len = len; > > + msg.buf = (char *)data; > > + > > + rc = i2c_transfer(adap, &msg, 1); > > + if (rc != 1) > > + dev_err(&ts->client->dev, > > + "[ELAN] i2c_transfer write fail, rc=%d\n", rc); > > + > > + mutex_unlock(&ts->tr_mutex); > > + > > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > > + transmitted, else error code. */ > > + return (rc == 1) ? len : rc; > > + > > +} > > + > > +/** > > + * @brief \b elan_ts_get_data - set command to TP. > > + * @param client : our i2c device > > + * @param data : data to be received from TP. > > + * @param len : command length usually. > > + * > > + * get data from our TP. > > + */ > > +static int elants_get_data( > > + struct i2c_client *client, > > + const u8 *buf, > > + size_t len) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + struct i2c_adapter *adap = client->adapter; > > + struct i2c_msg msg; > > + int rc = 0; > > + > > + dev_dbg(&client->dev, > > + "[ELAN] Enter: %s id:0x%x\n", __func__, ts->i2caddr); > > + > > + mutex_lock(&ts->tr_mutex); > > + > > + msg.addr = ts->i2caddr; > > + msg.flags = client->flags & I2C_M_TEN; > > + msg.flags |= I2C_M_RD; > > + msg.len = len; > > + msg.buf = (char *)buf; > > + > > + rc = i2c_transfer(adap, &msg, 1); > > + if (rc != 1) > > + dev_err(&client->dev, > > + "[ELAN] i2c_transfer read fail, rc=%d\n", rc); > > + > > + mutex_unlock(&ts->tr_mutex); > > + > > + /* If everything went ok (i.e. 1 msg transmitted), return #bytes > > + transmitted, else error code. */ > > + return (rc == 1) ? len : rc; > > +} > > + > > +/** > > + * @brief \b elants_is_iap - is it in iap mode ? > > + * @param client : our i2c device > > + * @param addr : i2c address > > + * > > + * check whether fw currently is in iap. > > + * @return : > > + * < 0 :normal mode > > + * > 0 :iap mode or abnormal. > > + */ > > +static int elants_is_iap( > > + struct i2c_client *client, > > + u16 addr) > > +{ > > + int rc; > > + uint8_t buf_recv[4] = {0}; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + ts->i2caddr = addr; > > + rc = elants_get_data(client, buf_recv, 4); > > + if ((rc < 0) || memcmp(buf_recv, iniap_packet, 4)) { > > + dev_err(&client->dev, > > + "[elan] %s: ID 0x%x can't boot IAP!\n", > > + __func__, addr); > > + rc = -EINVAL; > > + } > > + > > + ts->i2caddr = elan_i2c_master; > > + > > + return rc; > > +} > > + > > +static int elants_boot( > > + struct i2c_client *client, > > + u16 addr, > > + elan_boot_t type) > > +{ > > + int rc; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + uint8_t command[2][4] = { > > + {0x4D, 0x61, 0x69, 0x6E}, /* normal_command */ > > + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ > > + }; > > + > > + ts->i2caddr = addr; > > + rc = elants_set_data(client, command[(int32_t)type], 4); > > + if (rc != 4) { > > + if (type == E_BOOT_IAP) > > + dev_err(&client->dev, > > + "[elan] Boot IAP fail, error=%d\n", > > + rc); > > + else > > + dev_dbg(&client->dev, > > + "[elan] Boot normal fail, error=%d\n", > > + rc); > > + ts->i2caddr = elan_i2c_master; > > + return -EINVAL; > > + } > > + pr_info("[elan] Boot success -- 0x%x\n", addr); > > + ts->i2caddr = elan_i2c_master; > > + return 0; > > +} > > + > > +static int elants_enter_iap(struct i2c_client *client) > > +{ > > + int rc = 0, i; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + elan_hw_reset(client); > > + > > + /* Boot devices to IAP mode */ > > + for (i = 0; i < CHIP_NUM; i++) { > > + rc = elants_boot(client, tp_device_addr[i], E_BOOT_IAP); > > + if (rc < 0) > > + return rc; > > + } > > + > > + elan_msleep(10); > > + > > + /* Check if devices can enter IAP mode */ > > + for (i = 0; i < CHIP_NUM; i++) { > > + rc = elants_is_iap(client, tp_device_addr[i]); > > + if (rc < 0) > > + return rc; > > + } > > + > > + ts->iap_mode = IAP_MODE_ENABLE; > > + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; > > + ts->i2caddr = elan_i2c_master; > > + > > + return rc; > > +} > > + > > + > > +/** > > + * @brief \b elan_ts_async_recv_data - get TP status > > + * @param client : our i2c device > > + * @param cmd : asking command > > + * @param buf : result > > + * @param size : command length usually. > > + * > > + * set command type and TP will return its status in buf. > > + */ > > +static int elants_async_recv_data( > > + struct i2c_client *client, > > + const uint8_t *cmd, > > + uint8_t *buf, size_t tx_size, > > + size_t rx_size) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + dev_dbg(&client->dev, > > + "[ELAN] Enter: %s\n", __func__); > > + > > + if (buf == NULL) > > + return -EINVAL; > > + > > + mutex_lock(&ts->tr_mutex); > > + if ((i2c_master_send(client, cmd, tx_size)) != tx_size) { > > + dev_err(&client->dev, > > + "%s: i2c_master_send failed\n", __func__); > > + goto fail; > > + } > > + > > + if (unlikely(elan_ts_poll(client) < 0)) > > + goto fail; > > + else { > > + if (i2c_master_recv(client, buf, rx_size) != rx_size) > > + goto fail; > > + mutex_unlock(&ts->tr_mutex); > > + } > > + > > + return 0; > > + > > +fail: > > + mutex_unlock(&ts->tr_mutex); > > + return -EINVAL; > > +} > > + > > +/** > > + * @brief \b __get_bc_version - probe /INT to get new/old BC > > + * @param client : our i2c device > > + * > > + * 1 : new bootcode > > + * 0 : old bootcode > > + */ > > +static int __get_bc_version(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + /* wait 150ms to probe /INT and get old-BC, new-BC status */ > > + msleep(150); > > + > > + if (gpio_get_value(ts->intr_gpio) == 0) { > > + /* Old BC */ > > + pr_info("[ELAN]detect intr=>Old FW\n"); > > + old_bootcode = true; > > + } else { > > + /* New BC */ > > + pr_info("[elan]detect intr=>New FW\n"); > > + old_bootcode = false; > > + } > > + > > + return old_bootcode; > > +} > > + > > + > > +/** > > + * @brief __hello_packet_handler - hadle hello packet. > > + * @param client : our i2c device > > + * > > + * @return { >0 means success, > > + * otherwise fail. } > > + * > > + * Normal hello packet is {0x55, 0x55, 0x55, 0x55} > > + * recovery mode hello packet is {0x55, 0x55, 0x80, 0x80} > > + */ > > +static int __hello_packet_handler(struct i2c_client *client) > > +{ > > + int rc = 0, tries = 5; > > + uint8_t buf_recv[4] = {0}; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + > > +retry: /* wait INT for 1sec */ > > + rc = elan_ts_poll(client); > > + if (rc < 0) { > > + dev_err(&client->dev, > > + "%s: poll failed!\n", ELAN_DEV_NAME); > > + if (tries-- > 0) > > + goto retry; > > + } > > + > > + rc = elants_get_data(client, buf_recv, 4); > > + > > + elan_dbg(client, > > + "[ELAN] rc = %d Hello Packet: [0x%.2x 0x%.2x 0x%.2x 0x%.2x]\n", > > + rc, buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]); > > + > > + if (rc != 4) { > > + dev_err(&client->dev, > > + "[ELAN] %s: Try recovery because of no hello\n", > > + __func__); > > + > > + /* force TP FW into IAP mode. */ > > + elants_enter_iap(client); > > + return -EINVAL; > > + } > > + > > + /* new iap main-flow recovery mechanism 20120518*/ > > + if (memcmp(buf_recv, hello_packet, 4) || > > + elants_is_iap(client, elan_i2c_slave1) > 0) { > > + dev_err(&client->dev, > > + "[ELAN] got mainflow recovery message\n"); > > + elants_enter_iap(client); > > + return -EINVAL; > > + } > > + > > + ts->i2caddr = elan_i2c_master; > > + > > + return rc; > > +} > > + > > +static int __fw_packet_handler(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int rc, tries = 3; > > + const uint8_t cmd[] = {CMD_R_PKT, 0x00, 0x00, 0x01}; > > + uint8_t buf_recv[4] = {0x0}; > > + > > + /* Command not support in IAP recovery mode */ > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return 0; > > +retry: > > + rc = elants_acqurie_data(client, cmd, buf_recv, 4); > > + if (rc < 0) > > + return rc; > > + > > + if (buf_recv[0] == CMD_S_PKT) { > > + ts->major_fw_version = ((buf_recv[1] & 0x0f) << 4) | > > + ((buf_recv[2] & 0xf0) >> 4); > > + ts->minor_fw_version = ((buf_recv[2] & 0x0f) << 4) | > > + ((buf_recv[3] & 0xf0) >> 4); > > + > > + /* For 1+2, support this function since Solution version 0.3E(2012/2/23) */ > > + if ((ts->major_fw_version == 0x00 && > > + ts->minor_fw_version == 0x00) || > > + (ts->major_fw_version == 0xFF && > > + ts->minor_fw_version == 0xFF)) { > > + dev_err(&client->dev, > > + "\n\n[ELAN] FW version is empty, suggest IAP ELAN > chip\n\n"); > > + return -EINVAL; > > + } > > + > > + elan_dbg(client, > > + "[ELAN] TOUCH MAJOR FW VERSION 0x%02x\n", > > + ts->major_fw_version); > > + elan_dbg(client, > > + "[ELAN] TOUCH MINOR FW VERSION 0x%02x\n", > > + ts->minor_fw_version); > > + } else { > > + if (tries > 0) { > > + tries--; > > + goto retry; > > + } > > + ts->major_fw_version = 0xff; > > + ts->minor_fw_version = 0xff; > > + dev_err(&client->dev, > > + "\n\n[ELAN] FW version is empty, suggest IAP ELAN chip\n\n"); > > + return -EINVAL; > > + > > + } > > + > > + return 0; > > +} > > + > > +static int __touch_get_resolution(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int rc; > > + uint8_t buf_recv[17] = {0x0}; > > + const uint8_t get_resolution_cmd[] = { > > + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00 > > + }; > > + > > + /* Command not support in IAP recovery mode */ > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return 0; > > + > > + rc = elants_async_recv_data(client, get_resolution_cmd, buf_recv, > > + sizeof(get_r esolution_cmd), sizeof(buf_recv)); > > + if (rc < 0) > > + return -rc; > > + > > + > > + elan_dbg(client, > > + "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", > > + buf_recv[2], buf_recv[6], buf_recv[10], > > + buf_recv[3], buf_recv[7], buf_recv[0], rc); > > + > > + if (buf_recv[0] != 0x9B) > > + return -EINVAL; > > + > > + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", > > + buf_recv[2], buf_recv[6], buf_recv[10], > > + buf_recv[3], buf_recv[7], buf_recv[11], rc); > > + > > + > > + ts->rows = (buf_recv[2] + buf_recv[6] + buf_recv[10]); > > + ts->cols = (buf_recv[3] + buf_recv[7] + buf_recv[11]); > > + > > + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x\n", buf_recv[2], > > + buf_recv[6], buf_recv[10], > > + buf_recv[3], buf_recv[7], buf_recv[11]); > > + > > + if (ts->rows < 2 || ts->cols < 2) { > > + dev_err(&client->dev, > > + "[ELAN] Invalid resolution (%d, %d)\n", > > + ts->rows, ts->cols); > > + > > + /* set default resolution if TP information is wrong */ > > + ts->rows = ELAN_X_MAX; > > + ts->cols = ELAN_Y_MAX; > > + > > + rc = ret_fail; > > + } > > + > > + /* translate trace number to TSP resolution */ > > + ts->cols = ELAN_TS_RESOLUTION(ts->cols); > > + ts->rows = ELAN_TS_RESOLUTION(ts->rows); > > + > > + elan_dbg(client, > > + "[ELAN] resolution rows = 0x%02x, cols = 0x%02x\n", > > + ts->rows, ts->cols); > > + > > + return 0; > > +} > > + > > + > > +/** > > + * elan_touch_get_bc_ver - obtain bootcode data from device > > + * @client: the interface we are querying > > + > > + * > > + * Send a bootcode version command and fill the results into the > > + * elan device structures. Caller must hold the mutex > > + * > > + > > + * Returns 0 or an error code > > + */ > > +static int __elan_touch_get_bc_ver(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + const u8 get_bc_ver_cmd[] = {0x53, 0x10, 0x00, 0x01}; > > + u8 buf_recv[4]; > > + int rc; > > + > > + /* Command not support in IAP recovery mode */ > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return 0; > > + > > + rc = elants_acqurie_data(client, get_bc_ver_cmd, > > + buf_recv, sizeof(buf_recv)); > > + if (rc < 0) { > > + dev_err(&client->dev, > > + "elan_ts_acqurie_data failed: get_bc_ver\n"); > > + return rc; > > + } > > + > > + ts->major_bc_version = (((buf_recv[1] & 0x0f) << 4) | > > + ((buf_recv[2]&0xf0) >> 4)); > > + ts->minor_bc_version = (((buf_recv[2] & 0x0f) << 4) | > > + ((buf_recv[3]&0xf0) >> 4)); > > + > > + elan_dbg(client, > > + "ELAN TOUCH MAJOR BC VERSION 0x%02x\n", > > + ts->major_bc_version); > > + elan_dbg(client, > > + "ELAN TOUCH MINOR BC VERSION 0x%02x\n", > > + ts->minor_bc_version); > > + > > + return 0; > > +} > > + > > +static int __elan_fastboot(struct i2c_client *client, int *count) > > +{ > > + int rc = 0; > > + > > + rc = elants_boot(client, elan_i2c_slave1, E_BOOT_NORM); > > + if (rc < 0) { > > + if (*count > 0) > > + return -EAGAIN; > > + return -1; > > + } > > + > > + rc = elants_boot(client, elan_i2c_master, E_BOOT_NORM); > > + if (rc < 0) { > > + if (*count > 0) > > + return -EAGAIN; > > + return -1; > > + } > > + > > + msleep(100); > > + > > + return rc; > > +} > > + > > +/** > > + * @brief elan_open - open elan device > > + * @param input : input device > > + * > > + */ > > +static int elan_open(struct input_dev *input) > > +{ > > + struct elants_data *ts = input_get_drvdata(input); > > + > > + dev_err(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + /* wait probe work_func done */ > > + while ((ts->status & STA_INIT4) == 0) > > + elan_msleep(1); > > + > > + return 0; > > +} > > + > > +/** > > + * @brief elan_close - close input device > > + * @param input : input device > > + * > > + */ > > +static void elan_close(struct input_dev *input) > > +{ > > + struct elants_data *ts = input_get_drvdata(input); > > + > > + dev_err(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + return; > > +} > > + > > + > > +/** > > + * @brief \b elan_ts_setup - initialization process. > > + * @param client : our i2c client > > + * > > + * set our TP up > > + * -# reset > > + * -# hello packet > > + * -# fw version > > + * -# TP info (resolution) > > + * > > + */ > > +static int __devinit elants_setup(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int rc, tries = 5, count = 5; > > + int len = 0; > > + bool fastboot = true; > > + > > +retry: > > + /*! Reset */ > > + elan_hw_reset(client); > > + > > + ts->rx_size = queue_header_size; > > + > > + /* New BootCode */ > > + if (fastboot == true) { > > + rc = __elan_fastboot(client, &count); > > + if (rc < 0) { > > + if (rc == -EAGAIN && --count > 0) > > + goto retry; > > + else { > > + len += sprintf(ts->boot_log + len, > > + "serious bug, BC may be broken!!\n"); > > + return -1; > > + } > > + } > > + fastboot = 0; > > + } else { > > + /* Wait bootcode timeout 1 second + > > + Main-flow initial ~100ms */ > > + ssleep(2); > > + } > > + > > + /*! - elan hello packet init */ > > + rc = __hello_packet_handler(client); > > + if (rc < 0) { > > + /* Wrong hello_packet or polling fail, retry */ > > + if ((rc == -EINVAL || rc == -ETIMEDOUT) && --tries > 0) { > > + dev_err(&client->dev, > > + "[ELAN] retries=%d, rc=%d\n", > > + tries, rc); > > + goto retry; > > + } > > + > > + len += sprintf(ts->boot_log + len, > > + "retry=%d polling time-out\n", 5-tries); > > + > > + dev_err(&client->dev, > > + "hello packet error.\n"); > > + /* Go through down*/ > > + > > + /* if iap mode enable, return and wait for IAP */ > > + if (ts->iap_mode == IAP_MODE_ENABLE) { > > + len += sprintf(ts->boot_log + len, "IAP mode enable\n"); > > + return rc; > > + } > > + } > > + > > + elan_dbg(client, > > + "__hello_packet_handler ...\n"); > > + > > + /*! - elan fw version */ > > + rc = __fw_packet_handler(client); > > + if (rc < 0) { > > + dev_err(&client->dev, "firmware checking error.\n"); > > + len += sprintf(ts->boot_log+len, "fw_packet_error=%x:%x\n", > > + ts->major_fw_version, ts->minor_fw_version); > > + > > + if (rc == -EINVAL) { > > + len += sprintf(ts->boot_log+len, > > + "FW version is empty, IAP enable!!\n"); > > + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; > > + ts->iap_mode = IAP_MODE_ENABLE; > > + } > > + > > + /* Go through down*/ > > + } > > + > > + elan_dbg(client, "__fw_packet_handler...\n"); > > + > > + /*! - elan TP information */ > > + rc = __touch_get_resolution(client); > > + if (rc < 0) { > > + dev_err(&client->dev, > > + "TP information checking error.\n"); > > + len += sprintf(ts->boot_log+len, > > + "touch_get_resolution=%x:%x\n", > > + ts->rows, ts->cols); > > + /* Go through down*/ > > + } > > + > > + elan_dbg(client, > > + "__touch_get_resolution...\n"); > > + > > + > > + /* Get TS BootCode version */ > > + rc = __elan_touch_get_bc_ver(client); > > + if (rc < 0) { > > + dev_err(&client->dev, > > + "TP get BC version error.\n"); > > + len += sprintf(ts->boot_log+len, > > + "touch_get_BC_ver=%x:%x\n", > > + ts->major_bc_version, ts->major_bc_version); > > + /* Go through down*/ > > + } > > + > > + if (len == 0) > > + len += sprintf(ts->boot_log, "boot success!!\n"); > > + > > + return rc; > > + > > +} > > + > > + > > +static int elants_get_power_state(struct i2c_client *client) > > +{ > > + int rc = 0; > > + const uint8_t cmd[] = {CMD_R_PKT, 0x50, 0x00, 0x01}; > > + uint8_t buf[4], power_state; > > + > > + rc = elants_acqurie_data(client, cmd, buf, 4); > > + if (rc) > > + return rc; > > + > > + power_state = buf[1]; > > + elan_dbg(client, > > + "dump repsponse: %0x\n", power_state); > > + power_state = (power_state & PWR_STATE_MASK) >> 3; > > + elan_dbg(client, > > + "power state = %s\n", > > + power_state == PWR_STATE_DEEP_SLEEP ? > > + "Deep Sleep" : "Normal/Idle"); > > + > > + return power_state; > > +} > > + > > + > > +/** > > + * @brief \b elan_ts_recv_data - received TP data > > + * @param client: our i2c device > > + * @param buf : buffer for put received data > > + * > > + * received data from TP device. > > + */ > > +static int elants_recv_data(struct i2c_client *client, uint8_t *buf) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int rc = 0, bytes_to_recv; > > + > > + dev_dbg(&client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + if (buf == NULL) > > + return -EINVAL; > > + > > + mutex_lock(&ts->tr_mutex); > > + > > + bytes_to_recv = ts->rx_size; > > + rc = i2c_master_recv(client, buf, bytes_to_recv); > > + > > + dev_dbg(&client->dev, > > + "[ELAN] %d:%d:%x:%x, addr=%x\n", bytes_to_recv, rc, > > + buf[0], buf[34], client->addr); > > + dev_dbg(&client->dev, > > + "[ELAN] %x:%x:%x:%x:%x:%x\n", buf[0], buf[1], buf[2], > > + buf[3], buf[4], buf[5]); > > + > > + if (rc != bytes_to_recv) { > > + dev_err(&client->dev, > > + "%s: i2c_master_recv error?!\n", > > + __func__); > > + rc = -EINVAL; > > + } > > + mutex_unlock(&ts->tr_mutex); > > + > > + return rc; > > +} > > + > > +/** > > + * elan_touch_pull_frame - pull a frame from the fifo > > + * @ed: our elan touch device > > + * @ehr: return buffer for the header > > + * @buf: data buffer > > + * > > + * Pulls a frame from the FIFO into the provided ehr and data buffer. > > + * The data buffer must be at least cmd_response_len bytes long. > > + */ > > +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf) > > +{ > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + WARN_ON(kfifo_get(ts->fifo, buf, cmd_response_len) > > + != cmd_response_len); > > +#else > > + WARN_ON(kfifo_out_locked(&ts->fifo, buf, cmd_response_len, > > + &ts->rx_kfifo_lock) > > + != cmd_response_len); > > +#endif > > Target mainline. > > + return cmd_response_len; > > +} > > + > > + > > +/** > > + * elan_touch_fifo_clean_old - Make room for new frames > > + * @ed: our elan touch device > > + * @room: space needed > > + * > > + * Empty old frames out of the FIFO until we can fit the new one into > > + * the other end. > > + */ > > +static void elan_touch_fifo_clean_old(struct elants_data *ts, int room) > > +{ > > + u8 buffer[cmd_response_len]; > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + while (kfifo_len(ts->fifo) + room >= FIFO_SIZE) > > + elan_touch_pull_frame(ts, buffer); > > +#else > > + while (kfifo_len(&ts->fifo) + room >= FIFO_SIZE) > > + elan_touch_pull_frame(ts, buffer); > > +#endif > > Mainline. > > +} > > + > > + > > +/** @brief \b elan_ts_GetRepoInfo - parse Multi-queue report header > > + * @param client : our i2c device > > + * @param buf : buffer data > > + * > > + * parsing report header and get data length. > > + * > > + */ > > +static int elants_GetRepoInfo(struct i2c_client *client, uint8_t *buf) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + struct multi_queue_header *buff = (struct multi_queue_header *)buf; > > + const u8 wait_packet[4] = {0x64, 0x64, 0x64, 0x64}; > > + int times = 10, rc = 0; > > + > > + switch (buf[idx_finger_header]) { > > + case cmd_header_byte_hello: > > + if (!memcmp(buf, hello_packet, 4)) > > + ts->wdt_reset++; > > + return ret_cmdrsp; > > + case cmd_header_byte_response: > > + /* Queue the data, using the fifo lock to serialize the multiple > > + accesses to the FIFO */ > > + elan_dbg(client, > > + "[ELAN] recv cmd_header_byte_response\n"); > > + > > + mutex_lock(&ts->fifo_mutex); > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + if (kfifo_len(ts->fifo) + cmd_response_len >= FIFO_SIZE) > > +#else > > + if (kfifo_len(&ts->fifo) + cmd_response_len >= FIFO_SIZE) > > + > > +#endif > > + /* Make room, make room */ > > + elan_touch_fifo_clean_old(ts, cmd_response_len); > > + /* Push the data */ > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) > > + kfifo_put(ts->fifo, buf, cmd_response_len); > > +#else > > + kfifo_in_locked(&ts->fifo, buf, > > + cmd_response_len, &ts->rx_kfifo_lock); > > +#endif > > Alse mainline. > > + mutex_unlock(&ts->fifo_mutex); > > + > > + elan_dbg(client, > > + "[ELAN] wake_up [%02x:%02x:%02x:%02x]\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + wake_up(&ts->wait); > > + return ret_cmdrsp; > > + /* Buffer mode header */ > > + case queue_header_byte_normal: > > + elan_dbg(client, > > + "[ELAN] report_count=%d report_len=%d\n", > > + buff->report_count, buff->report_length); > > + if (likely(buff->report_count <= 3)) { > > + ts->mq_header.report_count = buff->report_count; > > + ts->mq_header.report_length = buff->report_length; > > + ts->rx_size = ts->mq_header.report_length; > > + } else > > + return ret_fail; > > + > > + break; > > + case queue_header_byte_wait: > > + dev_err(&client->dev, > > + "========queue_header_byte_wait %x:%x:%x:%x========\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + /*! BUGFIX: buff[0] might be wrong (0x63), check buff[1 ~ 3] is enough*/ > > + if (!memcmp(buf+1, &wait_packet[1], 3)) { > > + do { > > + udelay(30); > > + elants_recv_data(client, (uint8_t *)buff); > > + } while (buff->packet_id != queue_header_byte_normal && > > + --times > 0); > > + if (times > 0) > > + rc = elants_GetRepoInfo(client, (uint8_t *)buff); > > + else > > + return ret_fail; > > + elan_dbg(client, > > + "Detect Wait_Header:rx_size=%d, report_count=%d \ > > + report_len=%d\n", ts->rx_size, > > + ts->mq_header.report_count, > > + ts->mq_header.report_length); > > + > > + } else > > + dev_err(&client->dev, > > + "[ELAN] ERROR!! wait header:%x:%x:%x:%x\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + break; > > + /* Not buffer mode, it's single word mode */ > > + case queue_header_byte_Single: > > + ts->mq_header.report_count = 1; > > + ts->mq_header.report_length = IDX_PACKET_SIZE_WIDTH; > > + ts->rx_size = ts->mq_header.report_length; > > + return ts->rx_size; > > + break; > > + default: > > + dev_err(&client->dev, > > + "unknown multi-queue command!! --%x %x:%x:%x--\n", > > + buf[0], buf[1], buf[2], buf[3]); > > + ts->mq_header_fail++; > > + > > + /* If glitch causes frame error, drop all finger report */ > > + ts->rx_size = queue_packet_max_len; > > + elants_recv_data(client, (uint8_t *)buff); > > + return ret_fail; > > + } > > + > > + return ts->rx_size; > > +} > > + > > + > > +/** @brief \b elan_touch_checksum - Add for checksum mechanism > > + * @param client : our i2c device > > + * @param buf : buffer data > > + * > > + * caculating checksum for make sure all data validity. > > + * > > + */ > > +static int elan_touch_checksum(struct i2c_client *client, u8 *buf) > > +{ > > + u8 i = 0, checksum = 0; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + /*! FIXME: checksum wasn't including width byte */ > > + for (i = 0; i < IDX_PACKET_SIZE_WIDTH - 6; i++) > > + checksum = checksum + buf[i]; > > + > > + ts->checksum_correct = checksum; > > + > > + if (checksum != buf[idx_finger_width_checksum]) { > > + ts->checksum_fail++; > > + dev_err(&client->dev, > > + "elan touch checksum fail: %02x:%02x\n", > > + checksum, buf[idx_finger_width_checksum]); > > + return -EFAULT; > > + } > > + > > + return 0; > > +} > > + > > +/** > > + * @brief \b elan_touch_parse_fid - parse the 10 fid bits > > + * @param data : the input bit stream > > + * @param fid : an array of fid values > > + * > > + * Unpack the 10 bits into an array. > > + * > > + * FIXME: Review whether we can't just use << operators after making > > + * sure the bits are native endian ? > > + */ > > +static inline void elan_touch_parse_fid(u8 *data, u8 *fid) > > +{ > > + fid[0] = (data[0] & 0x01); > > + fid[1] = (data[0] & 0x02); > > + fid[2] = (data[0] & 0x04); > > + fid[3] = (data[0] & 0x08); > > + fid[4] = (data[0] & 0x10); > > + fid[5] = (data[0] & 0x20); > > + fid[6] = (data[0] & 0x40); > > + fid[7] = (data[0] & 0x80); > > + fid[8] = (data[1] & 0x10); > > + fid[9] = (data[1] & 0x20); > > +} > > + > > +/** > > + * @brief \b elan_touch_parse_wid - parse the 10 wid bits > > + * @param data : the input bit stream > > + * @param wid : an array of width level > > + * > > + * Unpack the 10 bits into an array. > > + * > > + */ > > +static inline void elan_touch_parse_wid(u8 *data, u8 *wid) > > +{ > > + wid[0] = (data[0] >> 4); > > + wid[1] = (data[0] & 0x0f); > > + wid[2] = (data[1] >> 4); > > + wid[3] = (data[1] & 0x0f); > > + wid[4] = (data[2] >> 4); > > + wid[5] = (data[2] & 0x0f); > > + wid[6] = (data[3] >> 4); > > + wid[7] = (data[3] & 0x0f); > > + wid[8] = (data[4] >> 4); > > + wid[9] = (data[4] & 0x0f); > > +} > > + > > + > > +static inline int elants_parse_xy( > > + uint8_t *data, > > + uint16_t *x, > > + uint16_t *y) > > +{ > > + *x = *y = 0; > > + > > + *x = (data[0] & 0xf0); > > + *x <<= 4; > > + *x |= data[1]; > > + > > + *y = (data[0] & 0x0f); > > + *y <<= 8; > > + *y |= data[2]; > > + > > + return 0; > > +} > > + > > +/* transition stage for lookup by table, maybe update finger report > > + using by Win8 50Byte format that having actual value for X, Y width */ > > +static inline int elan_lookup_wid( > > + u8 data, > > + u16 *w, > > + u16 *h) > > +{ > > + static u16 pre_w, pre_h, cur_w, cur_h; > > + const u8 width_lookup_table[15][2] = { > > + {3, 2}, {3, 2}, {6, 3}, > > + {6, 3}, {10, 3}, {15, 4}, > > + {15, 5}, {15, 5}, {15, 5}, > > + {15, 5}, {15, 5}, {15, 5}, > > + {15, 5}, {15, 5}, {15, 5}, > > + }; > > + > > + cur_w = width_lookup_table[data][0] * 17; > > + cur_h = width_lookup_table[data][1] * 17; > > + > > + /* IIR to balance w, h vaule, don't jitter too much */ > > + *w = (cur_w + pre_w) >> 1; > > + *h = (cur_h + pre_h) >> 1; > > + > > + pre_w = cur_w; > > + pre_h = cur_h; > > + > > + return 0; > > +} > > + > > +static int elan_mt_compute_slot(struct mt_device *td) > > +{ > > + int i; > > + for (i = 0; i < td->maxcontacts; ++i) { > > + if (td->slots[i].contactid == td->curdata.contactid && > > + td->slots[i].touch_state) > > + return i; > > + } > > + for (i = 0; i < td->maxcontacts; ++i) { > > + if (!td->slots[i].seen_in_this_frame && > > + !td->slots[i].touch_state) > > + return i; > > + } > > + /* should not occurs. If this happens that means > > + * that the device sent more touches that it says > > + * in the report descriptor. It is ignored then. */ > > + return -1; > > +} > > + > > +/* > > + * this function is called when a whole contact has been processed, > > + * so that it can assign it to a slot and store the data there > > + */ > > +static void elan_mt_complete_slot(struct mt_device *td) > > +{ > > + td->curdata.seen_in_this_frame = true; > > + if (td->curvalid) { > > + int slotnum = elan_mt_compute_slot(td); > > + > > + if (slotnum >= 0 && slotnum < td->maxcontacts) > > + td->slots[slotnum] = td->curdata; > > + } > > + td->num_received++; > > +} > > + > > +/* > > + * this function is called when a whole packet has been received and processed, > > + * so that it can decide what to send to the input layer. > > + */ > > + > > +#if (MT_TYPE_B == 0) > > +static void elan_mt_emit_event( > > + struct mt_device *td, > > + struct input_dev *input) > > +{ > > + struct elants_data *ts = > > + container_of(td, struct elants_data, td); > > + int i; > > + > > + for (i = 0; i < td->maxcontacts; ++i) { > > + struct mt_slot *s = &(td->slots[i]); > > + if (!s->seen_in_this_frame) { > > + if (s->touch_state) { > > + s->touch_state = false; > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) > > + /* ingore finger-up event since TF1 seems having problem */ > > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, > s->touch_state); > > + input_mt_sync(input); > > +#endif > > + } > > + s->seen_in_this_frame = false; > > + continue; > > + } > > + > > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->touch_state); > > + > > + if (s->touch_state) { > > + /* this finger is on the screen */ > > + int wide = (s->w > s->h); > > + /* divided by two to match visual scale of touch */ > > + int major = max(s->w, s->h) >> 1; > > + int minor = min(s->w, s->h) >> 1; > > + > > + elan_dbg(ts->client, > > + "[ELAN] i=%d x=%x y=%x w=%x h=%x, p=%x\n", > > + i, s->x, s->y, major, minor, s->p); > > + > > + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, s->contactid); > > + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); > > + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); > > + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); > > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) > > + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); > > +#endif > > + input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, major); > > + > > +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) > > + /* Can't not report on Android 2.2, 10th finger will disorder */ > > + input_event(input, EV_ABS, ABS_MT_WIDTH_MINOR, minor); > > +#endif > > + } > > + input_mt_sync(input); > > + s->seen_in_this_frame = false; > > + } > > + > > + input_sync(input); > > + td->num_received = 0; > > +} > > +#else /* For Android ICS, input protocol-B */ > > +static void elan_mt_emit_event( > > + struct mt_device *td, > > + struct input_dev *input) > > +{ > > + struct elants_data *ts = > > + container_of(td, struct elants_data, td); > > + int i; > > + > > + for (i = 0; i < td->maxcontacts; ++i) { > > + struct mt_slot *s = &(td->slots[i]); > > + if (!s->seen_in_this_frame) > > + s->touch_state = false; > > + > > + input_mt_slot(input, i); > > + input_mt_report_slot_state(input, MT_TOOL_FINGER, s->touch_state); > > + if (s->touch_state) { > > + /* this finger is on the screen */ > > + int wide = (s->w > s->h); > > + /* divided by two to match visual scale of touch */ > > + int major = max(s->w, s->h) >> 1; > > + int minor = min(s->w, s->h) >> 1; > > + > > + elan_dbg(ts->client, > > + "[ELAN] i=%d x=%x y=%x w=%x h=%x\n", > > + i, s->x, s->y, major, minor); > > + > > + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); > > + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); > > + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); > > + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); > > + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); > > + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); > > + } > > + s->seen_in_this_frame = false; > > + } > > + > > + input_mt_report_pointer_emulation(input, true); > > + input_sync(input); > > + td->num_received = 0; > > +} > > + > > +#endif > > + > > + > > +/** @brief \b elan_mt_event - process finger reports > > + * @param ts: our touchscreen > > + * @param finger_stat : number of fingers in packet > > + * @param buf : received buffer > > + * > > + * Walk the received report and process the finger data, extracting > > + * and reporting co-ordinates. No locking is needed here as the workqueue > > + * does our threading for us. > > + */ > > +static int elan_mt_event( > > + struct elants_data *ts, > > + int finger_stat, > > + u8 *buf) > > +{ > > + int i; > > + u8 fid[FINGER_NUM], wid[FINGER_NUM]; > > + u16 x, y, w, h; > > + struct i2c_client *client = ts->client; > > + struct mt_device *td = &ts->td; > > + > > + dev_dbg(&client->dev, > > + "[ELAN] Enter elan_mt_event Func\n"); > > + > > + /* Parsing Finger, Width field */ > > + elan_touch_parse_fid(&buf[idx_finger_state], &fid[0]); > > + elan_touch_parse_wid(&buf[idx_finger_width], &wid[0]); > > + > > + /* number of fingers expects in this frame */ > > + td->num_expected = finger_stat; > > + for (i = 0; i < td->maxcontacts; i++) { > > + if (finger_stat == 0) > > + break; > > + > > + /* tracking id */ > > + td->curdata.contactid = (fid[i] > 0) ? i+1 : 0; > > + > > + if (td->curdata.contactid == 0) > > + continue; > > + > > + td->curdata.touch_state = true; > > + > > + elants_parse_xy(&buf[3+i*3], &x, &y); > > + td->curdata.x = x; > > + td->curdata.y = y; > > + > > + elan_lookup_wid(wid[i], &w, &h); > > + td->curdata.w = w; > > + td->curdata.h = h; > > + > > + finger_stat--; > > + > > + elan_mt_complete_slot(td); > > + } > > + > > + if (td->num_received >= td->num_expected) > > + elan_mt_emit_event(td, ts->input); > > + > > + return 1; > > +} > > + > > + > > + > > +/** @brief \b elants_report_data - report finger report to user space. > > + * @param client : our i2c device > > + * @param buf : raw data from TP device. > > + * > > + * - reporting finger data to user space. > > + * - packet_fail count > > + * > > + */ > > +static void elants_report_data(struct i2c_client *client, uint8_t *buf) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + switch (buf[idx_finger_header]) { > > + case idx_coordinate_packet_4_finger: > > + case idx_coordinate_packet_10_finger: { > > + u8 finger_stat = buf[idx_finger_total] & 0x0f; > > + elan_dbg(client, > > + "[ELAN] finger_stat == %d\n", finger_stat); > > + elan_dbg(client, > > + "[ELAN] finger:%x:%x:%x:%x:%x\n", > > + buf[0], buf[1], buf[2], buf[3], buf[4]); > > + > > + /* Enter right process, reset int_status*/ > > + ts->polling.int_status = 0; > > + ts->packet_received++; > > + > > + if (likely(finger_stat != 0)) { > > + ts->td.curvalid = true; > > + /* Old way for report input event > > + elan_touch_report_fingers(ts , finger_stat, buf); */ > > + elan_mt_event(ts, finger_stat, buf); > > + ts->touched_sync++; > > + } else { > > + input_mt_sync(ts->input); > > + ts->no_touched_sync++; > > + input_sync(ts->input); > > + } > > + } > > + break; > > + default: > > + ts->header_fail++; > > + dev_err(&client->dev, > > + "[ELAN] %s: unknown packet type: %x:%x:%x:%x\n", > > + __func__, buf[0], buf[1], buf[2], buf[3]); > > + break; > > + } > > + > > + return; > > +} > > + > > +/** @brief \b elants_drop_packet - err handler that drop all packet. > > + * > > + * mutex protection will at outside this function. > > + * > > + */ > > +static void elants_drop_packet(struct elants_data *ts) > > +{ > > + uint8_t buf[queue_packet_max_len] = {0x0}; > > + > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return; > > + > > + if (gpio_get_value(ts->intr_gpio) != 0) > > + goto end; > > + > > + /* -# Read multi_queue header */ > > + ts->rx_size = queue_header_size; > > + if (elants_recv_data(ts->client, buf) < 0) > > + goto end; > > + > > + /* -# Get multi_queue header info*/ > > + if (elants_GetRepoInfo(ts->client, buf) > 0) { > > + /* 3. Get finger report data */ > > + elants_recv_data(ts->client, buf); > > + udelay(10); > > + if (gpio_get_value(ts->intr_gpio) != 0) > > + goto end; > > + } > > + > > + /* -#received all packet till ts->intr pull high */ > > + ts->rx_size = 1; > > + while (gpio_get_value(ts->intr_gpio) == 0) { > > + elants_recv_data(ts->client, buf); > > + udelay(10); > > + } > > + > > +end: > > + ts->rx_size = queue_header_size; > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Leave %s\n", __func__); > > + > > + return; > > +} > > + > > + > > + > > +/** @brief \b elan_ts_pollWork_func - regular processing > > + * @param work : pass from kernel > > + * > > + * poll_timer polled processing to occur elan_ts_pollWork_func() to check if our ts > > + * is abnormal. > > + * > > + */ > > +static void elants_pollWork_func(struct work_struct *work) > > + > > +{ > > + struct elants_data *ts = > > + container_of(work, struct elants_data, pollingwork); > > + > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + goto fail; > > + > > + if (gpio_get_value(ts->intr_gpio) != 0) > > + goto fail; > > + else { > > + ts->polling.int_status++; > > + if (ts->polling.int_status < 3) > > + goto fail; > > + } > > + > > + /* - we use mutex_trylock() here since polling is not very important */ > > + if (!mutex_trylock(&ts->mutex)) { > > + elan_dbg(ts->client, > > + "[ELAN] trylock fail! return...\n"); > > + goto fail; > > + } > > + > > + if (test_bit(idx_finger_report, &ts->busy)) { > > + elan_dbg(ts->client, > > + "[ELAN] 1.finger_Report processing ... ignore!!\n"); > > + goto unlock; > > + } > > + > > + if (test_bit(idx_cmd_handshake, &ts->busy)) { > > + elan_dbg(ts->client, > > + "[ELAN] 2.command processing ... ignore!!\n"); > > + goto unlock; > > + } > > + > > + dev_err(&ts->client->dev, > > + "[ELAN] Force to release intr_gpio!!\n"); > > + > > + ts->drop_frame++; > > + elants_drop_packet(ts); > > + > > +unlock: > > + mutex_unlock(&ts->mutex); > > + > > +fail: > > + return; > > +} > > + > > +static irqreturn_t elants_work_func(int irq, void *work) > > +{ > > + struct elants_data *ts = work; > > + uint8_t buf[queue_packet_max_len] = {0x0}; > > + u8 pos = 0, rc; > > + > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return IRQ_HANDLED; > > + > > + /* - this means that we have already serviced it or glich happen!! */ > > + if (gpio_get_value(ts->intr_gpio) != 0) { > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Leave %s(gpio)\n", __func__); > > + return IRQ_HANDLED; > > + } > > + > > + mutex_lock(&ts->mutex); > > + > > + set_bit(idx_finger_report, &ts->busy); > > + > > + /* Report one setting */ > > + /* ts->rx_size = 40; */ /* queue_header_size; */ > > + /* ts->mq_header.report_count=1; */ > > + > > + > > + /* - Read multi_queue header */ > > + if (elants_recv_data(ts->client, buf) < 0) > > + goto fail; > > + > > + /* - Get multi_queue header info */ > > + rc = elants_GetRepoInfo(ts->client, buf); > > + if (rc < 0 || rc == ret_cmdrsp) > > + goto fail; > > + > > + /* - Get finger report data */ > > + if (elants_recv_data(ts->client, buf) < 0) > > + goto fail; > > + > > + clear_bit(idx_finger_report, &ts->busy); > > + > > + mutex_unlock(&ts->mutex); > > + > > + /* - parsing report and send out */ > > + while (ts->mq_header.report_count--) { > > + if (elan_touch_checksum(ts->client, buf + pos) == 0) > > + elants_report_data(ts->client, buf + pos); > > + pos = pos + IDX_PACKET_SIZE_WIDTH; > > + udelay(10); > > + } > > + > > + ts->rx_size = queue_header_size; > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Leave %s\n", __func__); > > + return IRQ_HANDLED; > > + > > +fail: > > + clear_bit(idx_finger_report, &ts->busy); > > + mutex_unlock(&ts->mutex); > > + ts->rx_size = queue_header_size; > > + > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Leave %s(Fail)\n", __func__); > > + return IRQ_HANDLED; > > +} > > + > > + > > + > > +/** > > + * @brief \b elan_touch_timer_func - poll processing > > + * @param timer : our timer > > + * > > + * Queue polled processing to occur on our touch panel and kick the timer > > + * off again > > + * > > + * CHECK: we have no guarantee that the timer will not run multiple times > > + * within one poll, does it matter ? > > + */ > > +static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer) > > +{ > > + struct elants_data *ts = container_of(timer, struct elants_data, timer); > > + queue_work(ts->elan_wq, &ts->work); > > + hrtimer_start(&ts->timer, > > + ktime_set(0, 12500000), HRTIMER_MODE_REL); > > + > > + return HRTIMER_NORESTART; > > +} > > + > > +/** > > + * @brief \b elants_poll_timer_func - err handler when intr_gpio is low but no > isr serviced it. > > + * @param data : our ts > > + * > > + * intr_gpio polling checking, it'll force to get data if intr_gpio is low > > + * and not in isr routine. > > + * > > + */ > > +static void elants_poll_timer_func(unsigned long data) > > +{ > > + struct elants_data *ts = (struct elants_data *)data; > > + struct elan_polling *timer = &ts->polling; > > + > > + dev_dbg(&ts->client->dev, > > + "[ELAN] Enter %s\n", __func__); > > + > > + /* - ignore it if normal work is processing */ > > + if (work_pending(&ts->work)) { > > + dev_dbg(&ts->client->dev, > > + "[ELAN] 1.work_pending ... ignore!!\n"); > > + goto reset; > > + } > > + > > + /* - ignore it if poll_timer work is processing */ > > + if (work_pending(&ts->pollingwork)) { > > + dev_dbg(&ts->client->dev, > > + "[ELAN] 2.work_pending ... ignore!!\n"); > > + goto reset; > > + } > > + > > + if (!queue_work(timer->elan_wq, &ts->pollingwork)) > > + dev_err(&ts->client->dev, > > + "[ELAN] pollWork active failed!!\n"); > > + > > +reset: > > + timer->timer.expires = jiffies + 10 * HZ; > > + add_timer(&ts->polling.timer); > > + > > + return; > > +} > > + > > +static irqreturn_t elants_irq_handler(int irq, void *dev_id) > > +{ > > + struct elants_data *ts = dev_id; > > + struct i2c_client *client = ts->client; > > + > > + dev_dbg(&client->dev, > > + "[ELAN] %s\n", __func__); > > + > > + ts->irq_received++; > > + > > + return IRQ_WAKE_THREAD; > > +} > > + > > + > > +static int elants_register_interrupt(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + int err = 0; > > + > > + if (client->irq) { > > + > > + ts->use_irq = 1; > > + > > + err = request_threaded_irq(client->irq, elants_irq_handler, > > + elants_work_func, > > + IRQF_TRIGGER_FALLING | > IRQF_ONESHOT, > > + client->name, ts); > > + if (err) > > + dev_err(&client->dev, > > + "%s: request_irq %d failed\n", > > + __func__, client->irq); > > + > > + ts->status |= STA_USE_IRQ; > > + > > + elan_dbg(client, > > + "[ELAN] %s in interrupt mode\n", ts->input->name); > > + } > > + > > + if (!ts->use_irq) { > > + hrtimer_init(&ts->timer, > > + CLOCK_MONOTONIC, HRTIMER_MODE_REL); > > + ts->timer.function = elan_touch_timer_func; > > + hrtimer_start(&ts->timer, ktime_set(1, 0), > > + HRTIMER_MODE_REL); > > + elan_dbg(client, > > + "[ELAN] %s in time-polling mode\n", ts->input->name); > > + } > > + > > + return err; > > +} > > + > > +static int remove_elants(struct i2c_client *client) > > +{ > > + int ret = 0; > > + struct elants_data *ts = i2c_get_clientdata(client); > > + > > + if (&client->dev.kobj) > > + sysfs_remove_group(&client->dev.kobj, &elan_attribute_group); > > + > > +#ifdef CONFIG_HAS_EARLYSUSPEND > > + if (&ts->early_suspend) > > + unregister_early_suspend(&ts->early_suspend); > > +#endif > > + > > + if (ts->use_irq) { > > + if (client->irq) > > + free_irq(client->irq, ts); > > + } else { > > + if (&ts->timer) > > + hrtimer_cancel(&ts->timer); > > + } > > + > > +#if (MT_TYPE_B) > > + input_mt_destroy_slots(ts->input); > > +#endif > > + > > + if (ts->input) > > + input_unregister_device(ts->input); > > + > > + if (ts->elan_wq) > > + destroy_workqueue(ts->elan_wq); > > + > > + if (ts->polling.elan_wq) > > + destroy_workqueue(ts->polling.elan_wq); > > + > > + if (&ts->polling.timer) { > > + ret = del_timer(&ts->polling.timer); > > + if (ret != 0) > > + dev_err(&client->dev, > > + "[ELAN] del_timer fail!!\n"); > > + } > > + > > + if (&ts->mutex) > > + mutex_destroy(&ts->mutex); > > + if (&ts->tr_mutex) > > + mutex_destroy(&ts->tr_mutex); > > + if (&ts->fifo_mutex) > > + mutex_destroy(&ts->fifo_mutex); > > + > > + if (ts->fw_enabled) { > > + ret = misc_deregister(&ts->firmware); > > + if (ret < 0) > > + dev_err(&client->dev, > > + "[ELAN] misc_deregister fail!!\n"); > > + } > > + > > + kfree(ts->td.slots); > > + > > + /* free kfifo */ > > + kfifo_free(&ts->fifo); > > +#if (ANDROID) > > + wake_lock_destroy(&ts->wakelock); > > +#endif > > + > > + kfree(ts); > > + > > + return ret; > > +} > > + > > + > > +/** > > + * probe_thread_func - init touch device. > > + * @work: /int work queue > > + * > > + * Perform real probe for our I2C device and if successful configure > > + * it up as an input device. If not then clean up and return an error > > + * code. > > + */ > > + > > +static int probe_thread_func(void *data) > > +{ > > + struct elants_data *ts = data; > > + struct i2c_client *client = ts->client; > > + int err = 0; > > + > > + mutex_lock(&ts->mutex); > > + > > + err = elants_setup(client); > > + if (err < 0) { > > + dev_err(&client->dev, > > + "[ELAN] %s probe failed\n", > > + ELAN_DEV_NAME); > > + /* User can IAP anyway, continue */ > > + } > > + > > + set_bit(BTN_TOUCH, ts->input->keybit); > > + > > + /*! - Single touch input params setup */ > > + input_set_abs_params(ts->input, ABS_X, 0, ts->cols, 0, 0); > > + input_set_abs_params(ts->input, ABS_Y, 0, ts->rows, 0, 0); > > + input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0); > > + > > + /*! @warning FIXME: ABS_MT_PRESSURE can be supported by FW. > > + currently we have done it by ABS_MT_WIDTH_MAJOR */ > > + /*! - Multitouch input params setup */ > > + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->cols, 0, 0); > > + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->rows, 0, 0); > > +#if (MT_TYPE_B == 0) > > + input_set_abs_params(ts->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); > > + input_set_abs_params(ts->input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); > > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0); > > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 1, 0, 0); > > + > > + input_set_abs_params(ts->input, ABS_MT_TRACKING_ID, 0, FINGER_NUM, 0, > 0); > > +#else > > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); > > + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); > > + > > + /* not test, keep mark for less Android-ICS */ > > + input_set_abs_params(ts->input, ABS_MT_ORIENTATION, 0, 1, 0, 0); > > +#endif > > + > > + __set_bit(EV_ABS, ts->input->evbit); > > + __set_bit(EV_SYN, ts->input->evbit); > > + > > + input_set_drvdata(ts->input, ts); > > + > > + err = input_register_device(ts->input); > > + if (err) { > > + dev_err(&client->dev, > > + "[ELAN] %s unable to register input device\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + ts->td.slots = kzalloc(FINGER_NUM * sizeof(struct mt_slot), > > + GFP_KERNEL); > > + if (!ts->td.slots) { > > + dev_err(&client->dev, > > + "cannot allocate multitouch slots\n"); > > + goto fail_un; > > + } > > + ts->td.maxcontacts = FINGER_NUM; > > +#if (MT_TYPE_B) > > + input_mt_init_slots(ts->input, ts->td.maxcontacts); > > +#endif > > + > > + /*! @warning If the firmware device fails we carry on as it doesn't stop normal > > + usage */ > > + private_ts = &ts->firmware; > > + if (elants_register_interrupt(ts->client) < 0) { > > + dev_err(&client->dev, > > + "[ELAN] %s register_interrupt failed!\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + /*! - Register SW watchdog timer */ > > + init_timer(&ts->polling.timer); > > + /*! - check intr_gpio every 10secs */ > > + ts->polling.timer.expires = jiffies + 10 * HZ; > > + ts->polling.timer.function = &elants_poll_timer_func; > > + ts->polling.timer.data = (unsigned long)ts; > > + add_timer(&ts->polling.timer); > > + > > + mutex_unlock(&ts->mutex); > > + > > + ts->status |= STA_INIT4; > > + > > + return 0; > > + > > +fail_un: > > + mutex_unlock(&ts->mutex); > > + remove_elants(client); > > + return err; > > +} > > + > > + > > + > > +/** > > + * @brief elants_probe - probe for touchpad > > + * @param client : our I2C client > > + * > > + * Perform setup and probe for our I2C device and if successful configure > > + * it up as an input device. If not then clean up and return an error > > + * code. > > + */ > > +static int __devinit elants_probe( > > + struct i2c_client *client, > > + const struct i2c_device_id *id) > > +{ > > + long err = -1; > > + struct elan_i2c_platform_data *pdata = NULL; > > + struct elants_data *ts = > > + kzalloc(sizeof(struct elants_data), GFP_KERNEL); > > + > > + if (ts == NULL) > > + return -ENOMEM; > > + > > + ts->status |= STA_PROBED; > > + > > + mutex_init(&ts->mutex); > > + mutex_init(&ts->tr_mutex); > > + mutex_init(&ts->fifo_mutex); > > + init_waitqueue_head(&ts->wait); > > + spin_lock_init(&ts->rx_kfifo_lock); > > +#if (ANDROID) > > + wake_lock_init(&ts->wakelock, WAKE_LOCK_SUSPEND, "elan_touch"); > > +#endif > > + > > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > > + dev_err(&client->dev, > > + "[ELAN] %s: i2c check functionality error\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + /* @{ - Normal operatin (Finger report) */ > > + ts->elan_wq = create_singlethread_workqueue("elan_wq"); > > + if (!ts->elan_wq) { > > + dev_err(&client->dev, > > + "[ELAN] %s: create workqueue failed\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + /*! - Regular polling Process */ > > + ts->polling.elan_wq = create_singlethread_workqueue("elan_poll_wq"); > > + if (!ts->polling.elan_wq) { > > + dev_err(&client->dev, > > + "[ELAN] %s: create workqueue failed\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + /* INIT polling machinams */ > > + INIT_WORK(&ts->pollingwork, elants_pollWork_func); > > + > > + pdata = client->dev.platform_data; > > + if (!pdata) { > > + dev_err(&client->dev, > > + "[ELAN] %s no platform data\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + ts->status &= STA_INIT; > > + > > + ts->intr_gpio = pdata->intr_gpio; > > + ts->rst_gpio = pdata->rst_gpio; > > + /* set initial i2c address */ > > + client->addr = elan_i2c_master; > > + ts->i2caddr = client->addr; > > + > > + elan_dbg(client, > > + "reset=%d, intr=%d\n", ts->rst_gpio, ts->intr_gpio); > > + > > + ts->client = client; > > + i2c_set_clientdata(client, ts); > > + > > + /* INIT kfifo */ > > + err = kfifo_alloc(&ts->fifo, FIFO_SIZE, GFP_KERNEL); > > + if (!kfifo_initialized(&ts->fifo)) { > > + dev_err(&client->dev, > > + "%s error kfifo_alloc\n", __func__); > > + goto fail_un; > > + } > > + > > + > > + /*! - IAP Device Initial */ > > + ts->firmware.minor = MISC_DYNAMIC_MINOR; > > + ts->firmware.name = "elan-iap"; > > + ts->firmware.fops = &elan_touch_fops; > > + ts->firmware.mode = S_IRWXUGO; > > + > > + if (unlikely(misc_register(&ts->firmware) < 0)) > > + dev_err(&client->dev, > > + "[ELAN] IAP device register failed!!"); > > + else { > > + elan_dbg(client, > > + "[ELAN] IAP device register finished!!"); > > + ts->fw_enabled = 1; > > + } > > + > > + ts->status &= STA_INIT2; > > + > > + ts->input = input_allocate_device(); > > + if (ts->input == NULL) { > > + dev_err(&client->dev, > > + "[ELAN] %s Failed to allocate input device\n", > > + ELAN_DEV_NAME); > > + goto fail_un; > > + } > > + > > + ts->input->name = ELAN_DEV_NAME; > > + ts->input->open = elan_open; > > + ts->input->close = elan_close; > > + > > + /* Says HELLO to touch device */ > > + ts->thread = kthread_run(probe_thread_func, ts, client->name); > > + if (IS_ERR(ts->thread)) { > > + err = PTR_ERR(ts->thread); > > + goto fail_un; > > + } > > + > > + ts->status &= STA_INIT3; > > + > > +#ifdef CONFIG_HAS_EARLYSUSPEND > > + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING - 1; > > + ts->early_suspend.suspend = elants_early_suspend; > > + ts->early_suspend.resume = elants_late_resume; > > + register_early_suspend(&ts->early_suspend); > > +#endif > > + > > + /* - register sysfs @} */ > > + elan_dbg(client, > > + "[ELAN] create sysfs!!\n"); > > + if (sysfs_create_group(&client->dev.kobj, &elan_attribute_group)) > > + dev_err(&client->dev, > > + "[ELAN] sysfs create group error\n"); > > + > > + > > + return 0; > > + > > +fail_un: > > + remove_elants(client); > > + return err; > > +} > > + > > +static int __devexit elants_remove(struct i2c_client *client) > > +{ > > + return remove_elants(client); > > +} > > + > > +#ifdef CONFIG_PM_SLEEP > > +/** @brief \b elan_ts_suspend - enter sleep mode > > + * > > + * when Suspend mode, disable_irq and cancel work queue. > > + * > > + */ > > +static int elants_suspend(struct i2c_client *client, pm_message_t mesg) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + const u8 set_sleep_cmd[] = {0x54, 0x50, 0x00, 0x01}; > > + int rc = 0; > > + > > + dev_err(&client->dev, > > + "[ELAN] %s: enter\n", __func__); > > + > > + /* Command not support in IAP recovery mode */ > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return 0; > > + > > + disable_irq(client->irq); > > + cancel_work_sync(&ts->work); > > + > > + mutex_lock(&ts->mutex); > > + rc = elants_set_data(client, > > + set_sleep_cmd, sizeof(set_sleep_cmd)); > > + > > + if (rc < 0) > > + goto end; > > + > > + ts->power_state = 0; > > + /* de-active timer */ > > + del_timer_sync(&ts->polling.timer); > > + > > + > > +end: > > + mutex_unlock(&ts->mutex); > > + return rc; > > +} > > + > > + > > +/** @brief \b elan_ts_resume - enter wake-up mode > > + * > > + * when Wake-up mode, enable_irq. > > + * > > + */ > > +static int elants_resume(struct i2c_client *client) > > +{ > > + struct elants_data *ts = i2c_get_clientdata(client); > > + const u8 set_active_cmd[] = {0x54, 0x58, 0x00, 0x01}; > > + int rc = 0, retry = 5; > > + > > + dev_err(&client->dev, > > + "[ELAN] %s: enter\n", > > + __func__); > > + > > + /* Command not support in IAP recovery mode */ > > + if (ts->protocol & PRO_UPDATE_FW_MODE) > > + return 0; > > + > > + mutex_lock(&ts->mutex); > > + > > + do { > > + rc = elants_set_data(client, > > + set_active_cmd, sizeof(set_active_cmd)); > > + elan_msleep(2); > > + rc = elants_get_power_state(client); > > + if (unlikely(rc != PWR_STATE_NORMAL)) > > + dev_err(&client->dev, > > + "[ELAN] %s: wake up tp failed! err = %d\n", > > + __func__, rc); > > + else > > + break; > > + } while (--retry); > > + > > + ts->power_state = 1; > > + mutex_unlock(&ts->mutex); > > + > > + /* re-active poll timer */ > > + mod_timer(&ts->polling.timer, jiffies + 10 * HZ); > > + > > + enable_irq(client->irq); > > + > > + return rc; > > +} > > +#endif > > + > > + > > +#ifdef CONFIG_HAS_EARLYSUSPEND > > Convert to mainline PM functions. Use CONFIG_PM_SLEEP and > SIMPLE_DEV_PM_OPS > > +/** @brief \b elants_early_suspend - enter sleep mode > > + * > > + * This function is called by kernel, > > + * elan_ts_early_suspend() -> elan_ts_suspend() > > + * > > + */ > > +static void elants_early_suspend(struct early_suspend *h) > > +{ > > + struct elants_data *ts = > > + container_of(h, struct elants_data, early_suspend); > > + dev_dbg(&ts->client->dev, > > + "Enter %s\n", __func__); > > + > > + elants_suspend(ts->client, PMSG_SUSPEND); > > + return; > > +} > > + > > +/** @brief \b elants_late_resume - enter wake-up mode > > + * > > + * This function is called by kernel, > > + * elan_ts_late_resume() -> elan_ts_resume() > > + * > > + */ > > +static void elants_late_resume(struct early_suspend *h) > > +{ > > + struct elants_data *ts = > > + container_of(h, struct elants_data, early_suspend); > > + dev_dbg(&ts->client->dev, > > + "Enter %s\n", __func__); > > + > > + elants_resume(ts->client); > > + return; > > +} > > +#endif > > + > > +/*! brief system registeration */ > > +static const struct i2c_device_id elan_ts_id[] = { > > + { ELAN_DEV_NAME, 0 }, > > + { } > > +}; > > +MODULE_DEVICE_TABLE(i2c, elan_ts_id); > > + > > +/*! brief system registeration */ > > +static struct i2c_driver elan_ts_driver = { > > + .probe = elants_probe, > > + .remove = __devexit_p(elants_remove), > > +#ifndef CONFIG_HAS_EARLYSUSPEND > > + .suspend = elants_suspend, > > + .resume = elants_resume, > > +#endif > > + .id_table = elan_ts_id, > > + .driver = { > > + .name = ELAN_DEV_NAME, > > + .owner = THIS_MODULE, > > + .bus = &i2c_bus_type, > > + }, > > +}; > > + > > +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) > > +static int __devinit elan_ts_init(void) > > +{ > > + return i2c_add_driver(&elan_ts_driver); > > +} > > + > > +static void __exit elan_ts_exit(void) > > +{ > > + i2c_del_driver(&elan_ts_driver); > > + return; > > +} > > +module_init(elan_ts_init); > > +module_exit(elan_ts_exit); > > +#else > > +module_i2c_driver(elan_ts_driver); > > Target mainline and use this only. > > +#endif > > + > > +MODULE_VERSION(DRIVER_VERSION); > > +MODULE_DESCRIPTION("Elan TouchScreen Driver"); > > +MODULE_LICENSE("GPL"); > > diff --git a/include/linux/i2c/elants.h b/include/linux/i2c/elants.h > > new file mode 100644 > > index 0000000..5b45a0c > > --- /dev/null > > +++ b/include/linux/i2c/elants.h > > @@ -0,0 +1,61 @@ > > +#ifndef _LINUX_I2C_ELANTS_H > > +#define _LINUX_I2C_ELANTS_H > > + > > +#define ELAN_DEV_NAME "elants_i2c" > > + > > +/* set TSP resolution by actual TPM */ > > +#define ELAN_X_MAX 2944 > > +#define ELAN_Y_MAX 1856 > > + > > + > > +/*! @brief \b platform data > > + * @param intr_gpio : gpio pin > > + * @param rst_gpio : interuption pin > > + * > > + * Platform data, all platform dependent variable should put here. > > + * > > + */ > > +struct elan_i2c_platform_data { > > + unsigned short version; > > + unsigned int abs_x_min; > > + unsigned int abs_x_max; > > + unsigned int abs_y_min; > > + unsigned int abs_y_max; > > + int intr_gpio; > > + int rst_gpio; > > +}; > > + > > +struct elan_ioctl_data { > > + int len; > > + char buf[32]; > > +}; > > + > > +/*! @brief \b ioctl command definition. > > + * @param IOCTL_I2C_SLAVE : set i2c address that would be controled right now. > > + * @param IOCTL_MAJOR_FW_VER : fw major number > > + * @param IOCTL_MINOR_FW_VER : fw minor number > > + * @param IOCTL_RESET : Hardware Reset > > + * @param IOCTL_SET_COMMAND : control command set to TP device. > > + * @param IOCTL_GET_COMMAND : get response from our TP device. > > + * @param IOCTL_IAP_ENABLE : Enter IAP mode > > + * @param IOCTL_IAP_DISABLE : Leave IAP mode > > + * @param IOCTL_I2C_INT : gpio status > > + * > > + * > > + * ioctl command, easy user to control our TP from application. > > + * > > + */ > > +#define ELAN_IOCTLID 0xD0 > > +#define IOCTL_I2C_SLAVE _IOW(ELAN_IOCTLID, 1, int) > > +#define IOCTL_MAJOR_FW_VER _IOR(ELAN_IOCTLID, 2, int) > > +#define IOCTL_MINOR_FW_VER _IOR(ELAN_IOCTLID, 3, int) > > +#define IOCTL_RESET _IOW(ELAN_IOCTLID, 4, int) > > +#define IOCTL_I2C_INT _IOR(ELAN_IOCTLID, 8, int) > > +#define IOCTL_SET_COMMAND _IOW(ELAN_IOCTLID, 9, unsigned long) > > +#define IOCTL_GET_COMMAND _IOR(ELAN_IOCTLID, 10, unsigned long) > > +#define IOCTL_IAP_ENABLE _IOW(ELAN_IOCTLID, 11, int) > > +#define IOCTL_IAP_DISABLE _IOW(ELAN_IOCTLID, 12, int) > > +#define IOCTL_CHECK_RECOVERY_MODE _IOR(ELAN_IOCTLID, 13, int) > > +#define IOCTL_BOOTCODE_CMD _IOW(ELAN_IOCTLID, 14, unsigned long) > > + > > +#endif /* _LINUX_I2C_ELANTS_H */ > > -- > > 1.7.9.5 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > Please read the FAQ at http://www.tux.org/lkml/ > -- > To unsubscribe from this list: send the line "unsubscribe linux-input" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, > -----Original Message----- > From: Benjamin Tissoires [mailto:benjamin.tissoires@gmail.com] > Sent: Tuesday, October 23, 2012 1:12 AM > To: Dmitry Torokhov > Cc: Jian-Jhong Ding; Scott Liu; linux-input@vger.kernel.org; linux-i2c@vger.kernel.org; > linux-kernel@vger.kernel.org; Jesse; Vincent Wang; Paul > Subject: Re: [PATCH v1] Support Elan Touchscreen eKTF product. > > On Mon, Oct 22, 2012 at 6:07 PM, Dmitry Torokhov > <dmitry.torokhov@gmail.com> wrote: > > On Mon, Oct 22, 2012 at 11:47:42AM +0800, Jian-Jhong Ding wrote: > >> Scott Liu <scott.liu@emc.com.tw> writes: > >> > + > >> > +struct mt_device { > >> > + struct mt_slot curdata; /* placeholder of incoming data */ > >> > + __u8 num_received; /* how many contacts we received */ > >> > + __u8 num_expected; /* expected last contact index */ > >> > + __u8 maxcontacts; > >> > + bool curvalid; /* is the current contact valid? */ > >> > + struct mt_slot *slots; > >> > +}; > >> > >> With Benjamin's i2c-hid implimentation, is it possible to make > >> hid-multitouch not depend on USBHID and reuse it to drive this device? > > We can already use hid-multitouch with hid over I2C devices (I'm > testing my i2c devices with this module). > But it's true that hid-multitouch depends on usbhid, and I the funny > think is that I was removing this dependency today. > The fact is that currently, i2c devices do not segfault with > hid-multitouch because win8 devices do not require anymore to set > some feature at plug. > > > > > Exactly. Before looking any further - is this the same part that Tom Lin > > posted a driver for earlier this summer? > > I'm not Elan, and I can not be sure, but judging from the "hello > packets" and the other commands, I doubt this device is an I2C over > HID one. > It's an I2C device with ELAN own format, no HID packet transferring. > Anyway, reusing hid-multitouch for this specific case seams to be a > little bit difficult. You would have first to provide a fake report > descriptor (or inject commands as if you were hid-core) and then, you > would have to reformat the incoming data into valid win7 (or 8) > packets. Judging by the functions elan_touch_parse_fid, > elan_touch_parse_wid and elants_parse_xy, the data seem to be mixed > (all the finger ids at the beginning, and not one per touch), so it is > definitively needed to recreate a valid HID packet. > > So I'm not surprised with the duplicated code. However, Henrik did a > big job in kernel 3.7 to factorize mt code, and some part of the > duplication can be achieve with these functions (look at > input_mt_sync_frame and input_mt_get_slot_by_key for example). > I think so; it would cause too much pain for reuse hid-multitouch. BTW, I'll have checked Henrik's code to see if there are possible to reuse some code. Thanks, Scott > Cheers, > Benjamin > > > > > Thanks. > > > > -- > > Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Dmitry: This driver is not related to Tom's driver. This driver is ELAN standard I2C driver for touch panel and its packets is defined by ELAN own format and not related to HID over I2C. Thanks, Scott > -----Original Message----- > From: Jian-Jhong Ding [mailto:jj_ding@emc.com.tw] > Sent: Tuesday, October 23, 2012 10:15 AM > To: Dmitry Torokhov > Cc: Scott Liu; linux-input@vger.kernel.org; linux-i2c@vger.kernel.org; > linux-kernel@vger.kernel.org; Benjamin Tissoires; Jesse; Vincent Wang; Paul > Subject: Re: [PATCH v1] Support Elan Touchscreen eKTF product. > > Dmitry Torokhov <dmitry.torokhov@gmail.com> writes: > > On Mon, Oct 22, 2012 at 11:47:42AM +0800, Jian-Jhong Ding wrote: > >> With Benjamin's i2c-hid implimentation, is it possible to make > >> hid-multitouch not depend on USBHID and reuse it to drive this device? > > > > Exactly. Before looking any further - is this the same part that Tom Lin > > posted a driver for earlier this summer? > > No. Tom's driver was only for trackpads, this is probably for some > touch panel. The controller ICs may have some similarities, but the > firmware is developed independently, though both somewhat conform to the > HID over I2C protocol. > > Thanks, > -JJ > > Thanks. > > > > -- > > Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1ba232c..50e6f05 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -237,6 +237,15 @@ config TOUCHSCREEN_EETI To compile this driver as a module, choose M here: the module will be called eeti_ts. +config TOUCHSCREEN_ELAN + tristate "Elan touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected Elan touch panels. + + To compile this driver as a module, choose M here: the + module will be called elants_i2c. + config TOUCHSCREEN_EGALAX tristate "EETI eGalax multi-touch panel support" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 178eb12..428a631 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c new file mode 100644 index 0000000..d87b2ef --- /dev/null +++ b/drivers/input/touchscreen/elants_i2c.c @@ -0,0 +1,3182 @@ +/* + * Elan Microelectronics touchpanels with I2C interface + * + * Copyright (C) 2012 Elan Microelectronics Corporation. + * Scott Liu <scott.liu@emc.com.tw> + * + * This code is partly based on hid-multitouch.c: + * + * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> + * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France + * + */ + +/* + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + */ + +#define CHIP_NUM 2 + +/* Reserved for having platform to test */ +#define MT_TYPE_B 0 + +/* Define it if using on Android system */ +#define ANDROID 0 + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/interrupt.h> + +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/jiffies.h> +#include <linux/hrtimer.h> +#include <linux/timer.h> +#include <linux/kthread.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/buffer_head.h> +#include <linux/version.h> +#include <linux/kfifo.h> +#include <linux/slab.h> +#if (ANDROID) +#include <linux/wakelock.h> +#include <linux/earlysuspend.h> +#endif +#if (MT_TYPE_B) +#include <linux/input/mt.h> +#endif + +/* debug option */ +static bool debug = false; +module_param(debug, bool, 0444); +MODULE_PARM_DESC(debug, "print a lot of debug information"); + +#define elan_dbg(client, fmt, arg...) \ + if (debug) \ + dev_printk(KERN_DEBUG, &client->dev, fmt, ##arg) + +/*================================================= + * Marco + *================================================= */ +#define DRV_NAME "elants_i2c" + +#define DRV_MA_VER 2 +#define DRV_MI_VER 0 +#define DRV_SUB_MI_VER 0 + +#define _str(s) #s +#define str(s) _str(s) +#define DRIVER_VERSION str(DRV_MA_VER.DRV_MI_VER.DRV_SUB_MI_VER) + + +#define IDX_PACKET_SIZE_4FIG 17 +#define IDX_PACKET_SIZE_WIDTH 40 + +#define FINGER_NUM 10 + +#define PWR_STATE_DEEP_SLEEP 0 +#define PWR_STATE_NORMAL 1 +#define PWR_STATE_MASK BIT(3) + +#define CMD_S_PKT 0x52 +#define CMD_R_PKT 0x53 +#define CMD_W_PKT 0x54 + +#define HELLO_PKT 0x55 +#define NORMAL_PKT 0x62 + +#define RESET_PKT 0x77 +#define CALIB_PKT 0xA8 + +#define FINGER_ID 1 + +#define FIFO_SIZE (64) + + +/*! Convert from rows or columns into resolution */ +#define ELAN_TS_RESOLUTION(n) ((n - 1) * 64) + +static const char hello_packet[4] = { 0x55, 0x55, 0x55, 0x55 }; +static const char iniap_packet[4] = { 0x55, 0xaa, 0x33, 0xcc }; +static const char recov_packet[4] = { 0x55, 0x55, 0x80, 0x80 }; + +#include <linux/i2c/elants.h> + +/*! Firmware protocol status flag */ +#define PRO_I2C_WRT_CMD_SYNC 0x00000001 +#define PRO_HID_MOD_CHECKSUM 0x00000002 +#define PRO_UPDATE_FW_MODE 0x00000080 + + +/*! driver status flag, should move to public header file */ +#define STA_NONINIT 0x00000001 +#define STA_INIT 0x00000002 +#define STA_INIT2 0x00000004 +#define STA_INIT3 0x00000100 +#define STA_INIT4 0x00000200 +#define STA_PROBED 0x00000008 +#define STA_ERR_HELLO_PKT 0x00000010 +#define STA_USE_IRQ 0x00000020 +#define STA_SLEEP_MODE 0x00000040 + +/*======================================= + * Structure Definition + *=======================================*/ + +/*! @enum elan i2c address definition */ +enum elan_i2c_addr { + elan_i2c_master = 0x10, + elan_i2c_slave1 = 0x20, + elan_i2c_slave2 = 0x21, + + elan_i2c_maxnum = 3, +}; + + +/*! @enum finger_report_header Finger report header byte definition */ +enum finger_report_header { + idx_coordinate_packet_4_finger = 0x5c, + idx_coordinate_packet_10_finger = 0x62, +}; + +/*! @enum fw_queue_report_hdr FW Queue report header definition */ +enum fw_queue_report_hdr { + queue_header_byte_Single = 0x62, + queue_header_byte_normal = 0x63, + queue_header_byte_wait = 0x64, + queue_header_size = 4, + queue_packet_max_len = queue_header_size + (IDX_PACKET_SIZE_WIDTH * 3), +}; + +/*! @enum fw_normal_cmd_hdr FW Normal command header definition */ +enum fw_normal_cmd_hdr { + cmd_header_byte_write = 0x54, + cmd_header_byte_read = 0x53, + cmd_header_byte_response = 0x52, + cmd_header_byte_hello = 0x55, + cmd_response_len = 4 +}; + +/*! @enum fw_info_pos FW information position */ +enum fw_info_pos { + idx_finger_header = 0, + idx_finger_state = 1, + idx_finger_total = 2, + idx_finger_checksum = 34, + idx_finger_width = 35, + idx_4finger_checksum = 17, + idx_finger_width_checksum = 34, +}; + +/*! @enum lock_bit Lock bit definition */ +enum lock_bit { + idx_file_operate = 0, + idx_cmd_handshake = 1, + idx_finger_report = 2, + idx_test_mode = 3, +}; + +enum kfifo_ret { + ret_ok = 0, + ret_cmdrsp = 1, + ret_fail = -1, +}; + +typedef enum elan_boot_e { + E_BOOT_NORM = 0, + E_BOOT_IAP = 1 +} elan_boot_t; + +/* FW read command, 0x53 0x?? 0x0, 0x01 */ +enum { + E_ELAN_INFO_FW_VER = 0x00, + E_ELAN_INFO_BC_VER = 0x10, + E_ELAN_INFO_FW_ID = 0xf0 +}; + + +/*! @struct <multi_queue_header> */ +struct multi_queue_header { + u8 packet_id; + u8 report_count; + u8 report_length; + u8 reserved; +}; + +/*! @brief elan_polling */ +struct elan_polling { + struct workqueue_struct *elan_wq; /* polling work queue */ + struct timer_list timer; /* Polling intr_gpio timer */ + u8 int_status; /* polling intr gpio status */ +}; + + +/* finger handler, refer to hid-multitouch.c */ +struct mt_slot { + __s32 x, y, p, w, h; + __s32 contactid; /* the device ContactID assigned to this slot */ + bool touch_state; /* is the touch valid? */ + bool seen_in_this_frame;/* has this slot been updated */ +}; + +struct mt_device { + struct mt_slot curdata; /* placeholder of incoming data */ + __u8 num_received; /* how many contacts we received */ + __u8 num_expected; /* expected last contact index */ + __u8 maxcontacts; + bool curvalid; /* is the current contact valid? */ + struct mt_slot *slots; +}; + + +/** +* +* @brief elants_data +* +* all variable should include in this struct. +* +*/ +struct elants_data { + int intr_gpio; /* interupter pin*/ + int rst_gpio; /* reset pin*/ + int use_irq; + u8 major_fw_version; + u8 minor_fw_version; + u8 major_bc_version; + u8 minor_bc_version; + u8 major_hw_id; + u8 minor_hw_id; + bool fw_enabled; /* True if firmware device enabled*/ + int rows; /* Panel geometry for input layer*/ + int cols; + int x_max; + int y_max; + /* Power state 0:sleep 1:active*/ + u8 power_state; + /* TS is in IAP mode already*/ +#define IAP_MODE_ENABLE 1 + /* 1 : Firmware update mode + 0 : normal*/ + unsigned int iap_mode; + unsigned int rx_size; /* Read size in use*/ + + /* Multi-queue info */ + struct multi_queue_header mq_header; + + /* our i2c client*/ + struct i2c_client *client; + /* input device*/ + struct input_dev *input; + /* normal function work thread*/ + struct workqueue_struct *elan_wq; + /* normal function work queue*/ + struct work_struct work; + /* start probe start*/ + struct task_struct *thread; + /* char device for ioctl and IAP*/ + struct miscdevice firmware; + /* use timer if use_irq == 0*/ + struct hrtimer timer; + /* regular polling work thread*/ + struct work_struct pollingwork; + /* regular polling work queue*/ + struct elan_polling polling; +#if (ANDROID) + struct early_suspend early_suspend; + /* avoid sleep during IAP.*/ + struct wake_lock wakelock; +#endif + + /* Protects I2C accesses to device*/ + struct mutex mutex; + /* Protects I2C tx/rx*/ + struct mutex tr_mutex; + + /* Lock openers*/ + unsigned long busy; + /* Protocol stats for firmware*/ + unsigned int protocol; + + /* elan-iap i2c address*/ + unsigned short i2caddr; + + /* fifo and processing */ + struct kfifo fifo; + + /* Serialize operations around FIFO */ + struct mutex fifo_mutex; + wait_queue_head_t wait; + spinlock_t rx_kfifo_lock; + + /* boot log */ + u8 boot_log[256]; + + /*! Add for TS driver debug */ + unsigned int status; + long int irq_received; + long int packet_received; + long int packet_fail; + long int touched_sync; + long int no_touched_sync; + long int checksum_correct; + long int wdt_reset; + u16 checksum_fail; + u16 header_fail; + u16 mq_header_fail; + u16 drop_frame; + + /* mt device */ + struct mt_device td; +}; + + + +/*/============================================ + * Function prototype + *=============================================*/ +static int elants_set_data(struct i2c_client *client, + const u8 *data, + size_t len); +static int elants_async_recv_data(struct i2c_client *client, + const uint8_t *cmd, + uint8_t *buf, + size_t tx_size, + size_t rx_size); + +static void elants_drop_packet(struct elants_data *ts); +static irqreturn_t elants_work_func(int irq, void *work); + +static irqreturn_t elants_irq_handler(int irq, void *dev_id); +static int elan_hw_reset(struct i2c_client *client); +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void elants_early_suspend(struct early_suspend *h); +static void elants_late_resume(struct early_suspend *h); +#endif + + +/*================================================= + * Global variable + *=================================================*/ + +/*! for elan-iap char driver */ +static struct miscdevice *private_ts; +static bool old_bootcode = false; + +static u8 tp_device_addr[3] = {elan_i2c_master, + elan_i2c_slave1, + elan_i2c_slave2 + }; + + +#define elants_acqurie_data(a, cmd, buf, c) \ + elants_async_recv_data(a, cmd, buf, c, c) + +/*================================================= + * Function implement + *=================================================*/ +static inline void elan_msleep(u32 t) +{ + /* + * If the sleeping time is 10us - 20ms, usleep_range() is recommended. + * Read Documentation/timers/timers-howto.txt + */ + usleep_range(t*1000, t*1000 + 500); +} + +/** + * @brief __issue_bootcode_cmd - hadle bootcode status. + * @param client : our i2c device + * + * @return { >0 means success, + * otherwise fail. } + * + * Enter Normal mode packet is {0x55, 0x55, 0x55, 0x55} + * 2012/3/1 won't issue bootcmd in normal driver initial flow. + */ +static int elan_bootcode_cmd(struct i2c_client *client, + u8 passcode) +{ + int rc = 0; + struct elants_data *ts = i2c_get_clientdata(client); + u8 command[2][4] = { + {0x45, 0x49, 0x41, 0xFF}, /* normal_command */ + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ + }; + + /* Detect old / new FW */ + elan_msleep(90); + + if (gpio_get_value(ts->intr_gpio) == 0) { + /* Old FW */ + pr_warn("detect intr!!!!! OLD BC detect\n"); + return -2; + } + + if (passcode > E_BOOT_IAP) + return -1; + + elan_dbg(client, "passcode = %x:%x:%x:%x\n", + command[passcode][0], + command[passcode][1], + command[passcode][2], + command[passcode][3]); + + mutex_lock(&ts->mutex); + + ts->i2caddr = elan_i2c_slave2; + rc = elants_set_data(client, command[passcode], 4); + if (rc < 0) + dev_err(&ts->client->dev, + "[ELAN] set passcode[%d] fail\n", passcode); + + ts->i2caddr = elan_i2c_slave1; + rc = elants_set_data(client, command[passcode], 4); + if (rc < 0) + dev_err(&ts->client->dev, + "[ELAN] set passcode[%d] fail\n", passcode); + + ts->i2caddr = elan_i2c_master; + rc = elants_set_data(client, command[passcode], 4); + if (rc < 0) + dev_err(&ts->client->dev, + "[ELAN] set passcode[%d] fail\n", passcode); + + /* normal communitcat interactives with Master. */ + ts->i2caddr = elan_i2c_master; + mutex_unlock(&ts->mutex); + + return rc; +} + +/** +* @brief \b elan_iap_open - char device +* +* purpose for ioctl and iap updating fw. +*/ +int elan_iap_open(struct inode *inode, struct file *filp) +{ + struct elants_data *ts ; + + filp->private_data = private_ts; + ts = container_of(filp->private_data, + struct elants_data, firmware); + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + return 0; +} + +/** +* @brief \b elan_iap_release - char device +* +* purpose for close this device +*/ +int elan_iap_release(struct inode *inode, struct file *filp) +{ + struct elants_data *ts = + container_of(filp->private_data, + struct elants_data, firmware); + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + return 0; +} + +/** +* @brief \b elan_iap_write - iap write page data +* @param buff is page data from user space +* @param count is number of data in byte. +* +* purpose for iap write page data +*/ +ssize_t elan_iap_write(struct file *filp, + const char *buff, + size_t count, + loff_t *offp) +{ + struct elants_data *ts = + container_of(filp->private_data, + struct elants_data, firmware); + int ret; + u8 txbuf[256]; + struct i2c_adapter *adap = ts->client->adapter; + struct i2c_msg msg; + + if (count > 256) + return -EMSGSIZE; + + if (copy_from_user(txbuf, buff, count)) + return -EFAULT; + + msg.addr = ts->i2caddr; + msg.flags = ts->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (char *)txbuf; + + ret = i2c_transfer(adap, &msg, 1); + if (ret != 1) + dev_err(&ts->client->dev, + "[ELAN] i2c_master_send fail, ret=%d\n", ret); + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 1) ? count : ret; +} + +/** +* @brief \b elan_iap_read - read status code from TP +* +* purpose for iap read status code from TP +*/ +ssize_t elan_iap_read(struct file *filp, + char *buff, + size_t count, + loff_t *offp) +{ + struct elants_data *ts = + container_of(filp->private_data, + struct elants_data, firmware); + u8 rxbuf[256]; + int ret; + struct i2c_adapter *adap = ts->client->adapter; + struct i2c_msg msg; + + if (count > 256) + return -EMSGSIZE; + + msg.addr = ts->i2caddr; + msg.flags = ts->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = rxbuf; + + ret = i2c_transfer(adap, &msg, 1); + if (ret == 1) + if (copy_to_user(buff, rxbuf, count)) + return -EFAULT; + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 1) ? count : ret; +} + +/** +* @brief \b elan_iap_ioctl - ioctl +* @param cmd to control our TP device. +* @param arg is parameter from user space +* +* purpose is that control our TP by char device node. +*/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static int elan_iap_ioctl(struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg) +#else +static long elan_iap_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +#endif +{ + struct elants_data *ts = + container_of(filp->private_data, + struct elants_data, firmware); + int __user *argp = (int __user *)arg; + u8 len, buf[32]; + int rc; + + switch (cmd) { + case IOCTL_I2C_SLAVE: + ts->i2caddr = (unsigned short)arg; + break; + case IOCTL_MAJOR_FW_VER: + put_user(ts->major_fw_version, argp); + break; + case IOCTL_MINOR_FW_VER: + put_user(ts->minor_fw_version, argp); + break; + case IOCTL_CHECK_RECOVERY_MODE: + put_user(ts->iap_mode, argp); + break; + case IOCTL_IAP_ENABLE: { + disable_irq(ts->client->irq); +#if (ANDROID) + wake_lock(&ts->wakelock); +#endif + ts->protocol |= PRO_UPDATE_FW_MODE; + } + break; + case IOCTL_IAP_DISABLE: + ts->protocol &= ~PRO_UPDATE_FW_MODE; +#if (ANDROID) + wake_unlock(&ts->wakelock); +#endif + enable_irq(ts->client->irq); + break; + case IOCTL_BOOTCODE_CMD: { + int mode; + + len = 1; + if (copy_from_user(buf, (const void *)arg, len)) + return -EFAULT; + + mode = buf[0]; + rc = elan_bootcode_cmd(ts->client, (u8)mode); + if (rc < 0) { + elan_dbg(ts->client, + "elan_bootcode_command error\n"); + return -1; + } + } + break; + case IOCTL_RESET: + pr_warn("[ELAN] IOCTL_RESET\n"); + rc = elan_hw_reset(ts->client); + if (rc < 0) { + elan_dbg(ts->client, + "[ELAN] elan_hw_reset error\n"); + return -1; + } + break; + case IOCTL_I2C_INT: + put_user(gpio_get_value(ts->intr_gpio), argp); + break; + case IOCTL_SET_COMMAND: { + struct i2c_client *client = ts->client; + struct elan_ioctl_data idata; + + if (copy_from_user(&idata.len, + (const void *)arg, + sizeof(idata.len))) { + dev_err(&client->dev, + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", + buf[0], buf[1], buf[2], buf[3]); + return -EFAULT; + } + + if (copy_from_user(&idata, + (const void *)arg, + idata.len + sizeof(int))) { + dev_err(&client->dev, + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", + buf[0], buf[1], buf[2], buf[3]); + return -EFAULT; + } + dev_dbg(&ts->client->dev, + "IOCTL_SET_COMMAND:%x:%x:%x:%x\n", + idata.buf[0], + idata.buf[1], + idata.buf[2], + idata.buf[3]); + + if (idata.buf[0] != CMD_R_PKT && + idata.buf[0] != CMD_W_PKT) + return -EFAULT; + + mutex_lock(&ts->mutex); + elants_set_data(ts->client, + (const u8 *)idata.buf, + idata.len); + mutex_unlock(&ts->mutex); + } + break; + case IOCTL_GET_COMMAND: { + int rc = 0; + struct i2c_client *client = ts->client; + struct elan_ioctl_data idata; + + /* Get command length first */ + if (copy_from_user(&idata.len, + (const void *)arg, + sizeof(idata.len))) { + dev_err(&client->dev, + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", + buf[0], buf[1], buf[2], buf[3]); + return -EFAULT; + } + + /* Get command real length then */ + if (copy_from_user(&idata, (const void *)arg, + idata.len + sizeof(int))) { + dev_err(&client->dev, + "IOCTL_GET_COMMAND:%x:%x:%x:%x fail\n", + buf[0], buf[1], buf[2], buf[3]); + return -EFAULT; + } + + if (idata.buf[0] != CMD_R_PKT && idata.buf[0] != CMD_W_PKT) + return -EFAULT; + + pr_info("[ELAN] IOCTL_GET_COMMAND %x:%x:%x:%x\n", + idata.buf[0], idata.buf[1], idata.buf[2], idata.buf[3]); + + mutex_lock(&ts->mutex); + + set_bit(idx_cmd_handshake, &ts->busy); + + elants_set_data(client, idata.buf, idata.len); + mutex_unlock(&ts->mutex); + + /* We will wait for non O_NONBLOCK handles until a signal or data */ + mutex_lock(&ts->fifo_mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + while (kfifo_len(ts->fifo) == 0) { +#else + while (kfifo_len(&ts->fifo) == 0) { +#endif + mutex_unlock(&ts->fifo_mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + rc = wait_event_interruptible_timeout( + ts->wait, kfifo_len(ts->fifo), + msecs_to_jiffies(3000)); +#else + rc = wait_event_interruptible_timeout( + ts->wait, kfifo_len(&ts->fifo), + msecs_to_jiffies(3000)); +#endif + if (rc <= 0) { + rc = -ETIMEDOUT; + dev_err(&client->dev, + "timeout!! wake_up(ts->wait)\n"); + goto err2; + } + mutex_lock(&ts->fifo_mutex); + } + if (elan_touch_pull_frame(ts, idata.buf) < 0) { + rc = -1; + goto err1; + } + +err1: + mutex_unlock(&ts->fifo_mutex); +err2: + clear_bit(idx_cmd_handshake, &ts->busy); + + dev_dbg(&client->dev, + "[ELAN] Leave %s\n", __func__); + + if (rc < 0) { + rc = copy_to_user((void __user *)arg, + (void *)&idata, + 4+sizeof(len)); + return -rc; + } + + elan_dbg(client, + "Reponse=%x:%x:%x:%x\n", + idata.buf[0], + idata.buf[1], + idata.buf[2], + idata.buf[3]); + + if (copy_to_user((void __user *)arg, + (void *)&idata, + sizeof(idata))) + return -EFAULT; + + } + break; + default: + break; + } + + return 0; +} + +static const struct file_operations elan_touch_fops = { + .owner = THIS_MODULE, + .open = elan_iap_open, + .write = elan_iap_write, + .read = elan_iap_read, + .release = elan_iap_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = elan_iap_ioctl, +#else + .unlocked_ioctl = elan_iap_ioctl, +#endif +}; + + +/** + * @brief interfaces + * provide the hardware and firmware information + */ +static ssize_t show_fw_version_value( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + return sprintf(buf, "%.2x %.2x\n", + ts->major_fw_version, + ts->minor_fw_version); +} + + +static ssize_t show_bc_version_value( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%.2x %.2x\n", + ts->major_bc_version, + ts->minor_bc_version); +} + +static ssize_t show_drvver_value( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", DRIVER_VERSION); +} + + +static ssize_t show_intr_gpio( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + ret = gpio_get_value(ts->intr_gpio); + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t show_adapter_pkt_rvd( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + return sprintf(buf, "irq_received=%ld packet_received=%ld packet_fail=%ld \ + mq_hdr_fail=%d, checksum_fail=%x header_fail=%d, \ + poll_timer=%d, wdt_reset=%ld\n", + ts->irq_received, ts->packet_received, + ts->packet_fail, ts->mq_header_fail, + ts->checksum_fail, ts->header_fail, + ts->drop_frame, ts->wdt_reset); +} + +static ssize_t show_queue_count( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + return sprintf(buf, "queue_report_count=%d, report_length=%d\n", + ts->mq_header.report_count, ts->mq_header.report_length); +} + + +static ssize_t store_power_mode( + struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long val; + const char AcMode[] = {0x54, 0x30, 0x00, 0x01}; + const char BtMode[] = {0x54, 0x38, 0x00, 0x01}; + char cmd[4]; + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + switch (val) { + case 0: + memcpy(cmd, AcMode, sizeof(AcMode)); + break; + case 1: + memcpy(cmd, BtMode, sizeof(BtMode)); + break; + default: + return -1; + } + + mutex_lock(&ts->mutex); + elants_set_data(ts->client, cmd, sizeof(cmd)); + mutex_unlock(&ts->mutex); + + return count; +} + +static ssize_t show_power_mode( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const char RespCmd[] = {0x53, 0x30, 0x00, 0x01}; + char tbuf[4]; + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return -1; + + + mutex_lock(&ts->mutex); + set_bit(idx_cmd_handshake, &ts->busy); + elants_acqurie_data(ts->client, RespCmd, tbuf, sizeof(RespCmd)); + if (tbuf[0] != cmd_header_byte_response) { + dev_err(&client->dev, + "exception!! %x:%x:%x:%x\n", + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); + elants_drop_packet(ts); + memset(tbuf, 0x0, sizeof(tbuf)); + } + clear_bit(idx_cmd_handshake, &ts->busy); + mutex_unlock(&ts->mutex); + + return sprintf(buf, "%x:%x:%x:%x\n", + tbuf[0], tbuf[1], tbuf[2], tbuf[3]); +} + +static ssize_t show_report_rate( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + long int old_frame, new_frame; + + old_frame = ts->packet_received; + ssleep(1); + new_frame = ts->packet_received; + + return sprintf(buf, "%ld\n", new_frame - old_frame); +} + +static ssize_t show_iap_mode( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = + container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%s\n", + (ts->iap_mode == 0) ? "Normal" : "Recovery"); +} + +static ssize_t show_recal( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + u8 w_flashkey[4] = {0x54, 0xC0, 0xE1, 0x5A}; + u8 rek[4] = {0x54, 0x29, 0x00, 0x01}; + + ts->i2caddr = elan_i2c_master; + elants_set_data(client, w_flashkey, 4); + elan_msleep(1); + elants_set_data(client, rek, 4); + + return sprintf(buf, "%s\n", "re-K finish"); +} + + +static ssize_t show_boot_log( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct elants_data *ts = i2c_get_clientdata(client); + + /* show boot status, allow user cat this log during iap mode enable!! */ + + return scnprintf(buf, PAGE_SIZE, ts->boot_log); +} + + +static DEVICE_ATTR(power_mode, S_IRUGO|S_IWUGO, + show_power_mode, store_power_mode); +static DEVICE_ATTR(queue_count, S_IRUGO, show_queue_count, NULL); +static DEVICE_ATTR(drv_version, S_IRUGO, show_drvver_value, NULL); +static DEVICE_ATTR(fw_version, S_IRUGO, show_fw_version_value, NULL); +static DEVICE_ATTR(bc_version, S_IRUGO, show_bc_version_value, NULL); +static DEVICE_ATTR(ts_packet, S_IRUGO, show_adapter_pkt_rvd, NULL); +static DEVICE_ATTR(gpio, S_IRUGO, show_intr_gpio, NULL); +static DEVICE_ATTR(report_rate, S_IRUGO, show_report_rate, NULL); +static DEVICE_ATTR(boot_log, S_IRUGO, show_boot_log, NULL); +static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL); +static DEVICE_ATTR(rek, S_IRUGO, show_recal, NULL); + + + +static struct attribute *elan_attributes[] = { + &dev_attr_power_mode.attr, + &dev_attr_queue_count.attr, + &dev_attr_fw_version.attr, + &dev_attr_drv_version.attr, + &dev_attr_bc_version.attr, + &dev_attr_gpio.attr, + &dev_attr_ts_packet.attr, + &dev_attr_report_rate.attr, + &dev_attr_boot_log.attr, + &dev_attr_iap_mode.attr, + &dev_attr_rek.attr, + + NULL +}; + + +static struct attribute_group elan_attribute_group = { + .name = "elants", + .attrs = elan_attributes, +}; + + +/** + * @brief elan_hw_reset - h/w reset. + * @param client : our i2c device + * + * @retval >0 means reset success, \n + * otherwise is fail. + * + */ +static int elan_hw_reset(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int ret; + + if (ts->rst_gpio < 0) + return -1; + + ret = gpio_direction_output(ts->rst_gpio, 0); + if (ret < 0) { + pr_err("gpio_direction fail!\n"); + return ret; + } + + elan_msleep(2); + + ret = gpio_direction_output(ts->rst_gpio, 1); + if (ret < 0) + return ret; + + /* wait > 10ms to ensure that fw is in IAP mode */ + msleep(20); + + return ret; +} + + + +/** + * @brief elan_ts_poll - polling intr pin status. + * @param client : our i2c device + * + * @retval 0 means intr pin is low, \n + * otherwise is high. + * + * polling intr pin untill low and the maximus wait time is \b 200ms. + */ +static int elan_ts_poll(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int status = 0, retry = 20; + + do { + status = gpio_get_value(ts->intr_gpio); + elan_dbg(client, + "%s: status = %d\n", __func__, status); + retry--; + mdelay(10); + } while (status != 0 && retry > 0); + + elan_dbg(client, + "%s: poll interrupt status %s\n", + __func__, status == 1 ? "high" : "low"); + return (status == 0 ? 0 : -ETIMEDOUT); +} + +/** + * @brief \b elan_ts_set_data - set command to TP. + * @param client : our i2c device + * @param data : command or data which will send to TP. + * @param len : command length usually. + * + * set command to our TP. + */ +static int elants_set_data(struct i2c_client *client, + const u8 *data, size_t len) +{ + struct elants_data *ts = i2c_get_clientdata(client); + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int rc = 0; + + dev_dbg(&client->dev, "[ELAN] Enter: %s\n", __func__); + elan_dbg(client, + "dump cmd: %02x, %02x, %02x, %02x, addr=%x\n", + data[0], data[1], data[2], data[3], ts->i2caddr); + + mutex_lock(&ts->tr_mutex); + + msg.addr = ts->i2caddr; + msg.flags = ts->client->flags & I2C_M_TEN; + msg.len = len; + msg.buf = (char *)data; + + rc = i2c_transfer(adap, &msg, 1); + if (rc != 1) + dev_err(&ts->client->dev, + "[ELAN] i2c_transfer write fail, rc=%d\n", rc); + + mutex_unlock(&ts->tr_mutex); + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (rc == 1) ? len : rc; + +} + +/** + * @brief \b elan_ts_get_data - set command to TP. + * @param client : our i2c device + * @param data : data to be received from TP. + * @param len : command length usually. + * + * get data from our TP. + */ +static int elants_get_data( + struct i2c_client *client, + const u8 *buf, + size_t len) +{ + struct elants_data *ts = i2c_get_clientdata(client); + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int rc = 0; + + dev_dbg(&client->dev, + "[ELAN] Enter: %s id:0x%x\n", __func__, ts->i2caddr); + + mutex_lock(&ts->tr_mutex); + + msg.addr = ts->i2caddr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = len; + msg.buf = (char *)buf; + + rc = i2c_transfer(adap, &msg, 1); + if (rc != 1) + dev_err(&client->dev, + "[ELAN] i2c_transfer read fail, rc=%d\n", rc); + + mutex_unlock(&ts->tr_mutex); + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (rc == 1) ? len : rc; +} + +/** + * @brief \b elants_is_iap - is it in iap mode ? + * @param client : our i2c device + * @param addr : i2c address + * + * check whether fw currently is in iap. + * @return : + * < 0 :normal mode + * > 0 :iap mode or abnormal. + */ +static int elants_is_iap( + struct i2c_client *client, + u16 addr) +{ + int rc; + uint8_t buf_recv[4] = {0}; + struct elants_data *ts = i2c_get_clientdata(client); + + ts->i2caddr = addr; + rc = elants_get_data(client, buf_recv, 4); + if ((rc < 0) || memcmp(buf_recv, iniap_packet, 4)) { + dev_err(&client->dev, + "[elan] %s: ID 0x%x can't boot IAP!\n", + __func__, addr); + rc = -EINVAL; + } + + ts->i2caddr = elan_i2c_master; + + return rc; +} + +static int elants_boot( + struct i2c_client *client, + u16 addr, + elan_boot_t type) +{ + int rc; + struct elants_data *ts = i2c_get_clientdata(client); + uint8_t command[2][4] = { + {0x4D, 0x61, 0x69, 0x6E}, /* normal_command */ + {0x45, 0x49, 0x41, 0x50}, /* iap_command */ + }; + + ts->i2caddr = addr; + rc = elants_set_data(client, command[(int32_t)type], 4); + if (rc != 4) { + if (type == E_BOOT_IAP) + dev_err(&client->dev, + "[elan] Boot IAP fail, error=%d\n", + rc); + else + dev_dbg(&client->dev, + "[elan] Boot normal fail, error=%d\n", + rc); + ts->i2caddr = elan_i2c_master; + return -EINVAL; + } + pr_info("[elan] Boot success -- 0x%x\n", addr); + ts->i2caddr = elan_i2c_master; + return 0; +} + +static int elants_enter_iap(struct i2c_client *client) +{ + int rc = 0, i; + struct elants_data *ts = i2c_get_clientdata(client); + + elan_hw_reset(client); + + /* Boot devices to IAP mode */ + for (i = 0; i < CHIP_NUM; i++) { + rc = elants_boot(client, tp_device_addr[i], E_BOOT_IAP); + if (rc < 0) + return rc; + } + + elan_msleep(10); + + /* Check if devices can enter IAP mode */ + for (i = 0; i < CHIP_NUM; i++) { + rc = elants_is_iap(client, tp_device_addr[i]); + if (rc < 0) + return rc; + } + + ts->iap_mode = IAP_MODE_ENABLE; + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; + ts->i2caddr = elan_i2c_master; + + return rc; +} + + +/** + * @brief \b elan_ts_async_recv_data - get TP status + * @param client : our i2c device + * @param cmd : asking command + * @param buf : result + * @param size : command length usually. + * + * set command type and TP will return its status in buf. + */ +static int elants_async_recv_data( + struct i2c_client *client, + const uint8_t *cmd, + uint8_t *buf, size_t tx_size, + size_t rx_size) +{ + struct elants_data *ts = i2c_get_clientdata(client); + dev_dbg(&client->dev, + "[ELAN] Enter: %s\n", __func__); + + if (buf == NULL) + return -EINVAL; + + mutex_lock(&ts->tr_mutex); + if ((i2c_master_send(client, cmd, tx_size)) != tx_size) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __func__); + goto fail; + } + + if (unlikely(elan_ts_poll(client) < 0)) + goto fail; + else { + if (i2c_master_recv(client, buf, rx_size) != rx_size) + goto fail; + mutex_unlock(&ts->tr_mutex); + } + + return 0; + +fail: + mutex_unlock(&ts->tr_mutex); + return -EINVAL; +} + +/** + * @brief \b __get_bc_version - probe /INT to get new/old BC + * @param client : our i2c device + * + * 1 : new bootcode + * 0 : old bootcode + */ +static int __get_bc_version(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + + /* wait 150ms to probe /INT and get old-BC, new-BC status */ + msleep(150); + + if (gpio_get_value(ts->intr_gpio) == 0) { + /* Old BC */ + pr_info("[ELAN]detect intr=>Old FW\n"); + old_bootcode = true; + } else { + /* New BC */ + pr_info("[elan]detect intr=>New FW\n"); + old_bootcode = false; + } + + return old_bootcode; +} + + +/** + * @brief __hello_packet_handler - hadle hello packet. + * @param client : our i2c device + * + * @return { >0 means success, + * otherwise fail. } + * + * Normal hello packet is {0x55, 0x55, 0x55, 0x55} + * recovery mode hello packet is {0x55, 0x55, 0x80, 0x80} + */ +static int __hello_packet_handler(struct i2c_client *client) +{ + int rc = 0, tries = 5; + uint8_t buf_recv[4] = {0}; + struct elants_data *ts = i2c_get_clientdata(client); + + +retry: /* wait INT for 1sec */ + rc = elan_ts_poll(client); + if (rc < 0) { + dev_err(&client->dev, + "%s: poll failed!\n", ELAN_DEV_NAME); + if (tries-- > 0) + goto retry; + } + + rc = elants_get_data(client, buf_recv, 4); + + elan_dbg(client, + "[ELAN] rc = %d Hello Packet: [0x%.2x 0x%.2x 0x%.2x 0x%.2x]\n", + rc, buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]); + + if (rc != 4) { + dev_err(&client->dev, + "[ELAN] %s: Try recovery because of no hello\n", + __func__); + + /* force TP FW into IAP mode. */ + elants_enter_iap(client); + return -EINVAL; + } + + /* new iap main-flow recovery mechanism 20120518*/ + if (memcmp(buf_recv, hello_packet, 4) || + elants_is_iap(client, elan_i2c_slave1) > 0) { + dev_err(&client->dev, + "[ELAN] got mainflow recovery message\n"); + elants_enter_iap(client); + return -EINVAL; + } + + ts->i2caddr = elan_i2c_master; + + return rc; +} + +static int __fw_packet_handler(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int rc, tries = 3; + const uint8_t cmd[] = {CMD_R_PKT, 0x00, 0x00, 0x01}; + uint8_t buf_recv[4] = {0x0}; + + /* Command not support in IAP recovery mode */ + if (ts->protocol & PRO_UPDATE_FW_MODE) + return 0; +retry: + rc = elants_acqurie_data(client, cmd, buf_recv, 4); + if (rc < 0) + return rc; + + if (buf_recv[0] == CMD_S_PKT) { + ts->major_fw_version = ((buf_recv[1] & 0x0f) << 4) | + ((buf_recv[2] & 0xf0) >> 4); + ts->minor_fw_version = ((buf_recv[2] & 0x0f) << 4) | + ((buf_recv[3] & 0xf0) >> 4); + + /* For 1+2, support this function since Solution version 0.3E(2012/2/23) */ + if ((ts->major_fw_version == 0x00 && + ts->minor_fw_version == 0x00) || + (ts->major_fw_version == 0xFF && + ts->minor_fw_version == 0xFF)) { + dev_err(&client->dev, + "\n\n[ELAN] FW version is empty, suggest IAP ELAN chip\n\n"); + return -EINVAL; + } + + elan_dbg(client, + "[ELAN] TOUCH MAJOR FW VERSION 0x%02x\n", + ts->major_fw_version); + elan_dbg(client, + "[ELAN] TOUCH MINOR FW VERSION 0x%02x\n", + ts->minor_fw_version); + } else { + if (tries > 0) { + tries--; + goto retry; + } + ts->major_fw_version = 0xff; + ts->minor_fw_version = 0xff; + dev_err(&client->dev, + "\n\n[ELAN] FW version is empty, suggest IAP ELAN chip\n\n"); + return -EINVAL; + + } + + return 0; +} + +static int __touch_get_resolution(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int rc; + uint8_t buf_recv[17] = {0x0}; + const uint8_t get_resolution_cmd[] = { + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* Command not support in IAP recovery mode */ + if (ts->protocol & PRO_UPDATE_FW_MODE) + return 0; + + rc = elants_async_recv_data(client, get_resolution_cmd, buf_recv, + sizeof(get_resolution_cmd), sizeof(buf_recv)); + if (rc < 0) + return -rc; + + + elan_dbg(client, + "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", + buf_recv[2], buf_recv[6], buf_recv[10], + buf_recv[3], buf_recv[7], buf_recv[0], rc); + + if (buf_recv[0] != 0x9B) + return -EINVAL; + + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n", + buf_recv[2], buf_recv[6], buf_recv[10], + buf_recv[3], buf_recv[7], buf_recv[11], rc); + + + ts->rows = (buf_recv[2] + buf_recv[6] + buf_recv[10]); + ts->cols = (buf_recv[3] + buf_recv[7] + buf_recv[11]); + + elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x\n", buf_recv[2], + buf_recv[6], buf_recv[10], + buf_recv[3], buf_recv[7], buf_recv[11]); + + if (ts->rows < 2 || ts->cols < 2) { + dev_err(&client->dev, + "[ELAN] Invalid resolution (%d, %d)\n", + ts->rows, ts->cols); + + /* set default resolution if TP information is wrong */ + ts->rows = ELAN_X_MAX; + ts->cols = ELAN_Y_MAX; + + rc = ret_fail; + } + + /* translate trace number to TSP resolution */ + ts->cols = ELAN_TS_RESOLUTION(ts->cols); + ts->rows = ELAN_TS_RESOLUTION(ts->rows); + + elan_dbg(client, + "[ELAN] resolution rows = 0x%02x, cols = 0x%02x\n", + ts->rows, ts->cols); + + return 0; +} + + +/** + * elan_touch_get_bc_ver - obtain bootcode data from device + * @client: the interface we are querying + + * + * Send a bootcode version command and fill the results into the + * elan device structures. Caller must hold the mutex + * + + * Returns 0 or an error code + */ +static int __elan_touch_get_bc_ver(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + const u8 get_bc_ver_cmd[] = {0x53, 0x10, 0x00, 0x01}; + u8 buf_recv[4]; + int rc; + + /* Command not support in IAP recovery mode */ + if (ts->protocol & PRO_UPDATE_FW_MODE) + return 0; + + rc = elants_acqurie_data(client, get_bc_ver_cmd, + buf_recv, sizeof(buf_recv)); + if (rc < 0) { + dev_err(&client->dev, + "elan_ts_acqurie_data failed: get_bc_ver\n"); + return rc; + } + + ts->major_bc_version = (((buf_recv[1] & 0x0f) << 4) | + ((buf_recv[2]&0xf0) >> 4)); + ts->minor_bc_version = (((buf_recv[2] & 0x0f) << 4) | + ((buf_recv[3]&0xf0) >> 4)); + + elan_dbg(client, + "ELAN TOUCH MAJOR BC VERSION 0x%02x\n", + ts->major_bc_version); + elan_dbg(client, + "ELAN TOUCH MINOR BC VERSION 0x%02x\n", + ts->minor_bc_version); + + return 0; +} + +static int __elan_fastboot(struct i2c_client *client, int *count) +{ + int rc = 0; + + rc = elants_boot(client, elan_i2c_slave1, E_BOOT_NORM); + if (rc < 0) { + if (*count > 0) + return -EAGAIN; + return -1; + } + + rc = elants_boot(client, elan_i2c_master, E_BOOT_NORM); + if (rc < 0) { + if (*count > 0) + return -EAGAIN; + return -1; + } + + msleep(100); + + return rc; +} + +/** + * @brief elan_open - open elan device + * @param input : input device + * + */ +static int elan_open(struct input_dev *input) +{ + struct elants_data *ts = input_get_drvdata(input); + + dev_err(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + /* wait probe work_func done */ + while ((ts->status & STA_INIT4) == 0) + elan_msleep(1); + + return 0; +} + +/** + * @brief elan_close - close input device + * @param input : input device + * + */ +static void elan_close(struct input_dev *input) +{ + struct elants_data *ts = input_get_drvdata(input); + + dev_err(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + return; +} + + +/** + * @brief \b elan_ts_setup - initialization process. + * @param client : our i2c client + * + * set our TP up + * -# reset + * -# hello packet + * -# fw version + * -# TP info (resolution) + * + */ +static int __devinit elants_setup(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int rc, tries = 5, count = 5; + int len = 0; + bool fastboot = true; + +retry: + /*! Reset */ + elan_hw_reset(client); + + ts->rx_size = queue_header_size; + + /* New BootCode */ + if (fastboot == true) { + rc = __elan_fastboot(client, &count); + if (rc < 0) { + if (rc == -EAGAIN && --count > 0) + goto retry; + else { + len += sprintf(ts->boot_log + len, + "serious bug, BC may be broken!!\n"); + return -1; + } + } + fastboot = 0; + } else { + /* Wait bootcode timeout 1 second + + Main-flow initial ~100ms */ + ssleep(2); + } + + /*! - elan hello packet init */ + rc = __hello_packet_handler(client); + if (rc < 0) { + /* Wrong hello_packet or polling fail, retry */ + if ((rc == -EINVAL || rc == -ETIMEDOUT) && --tries > 0) { + dev_err(&client->dev, + "[ELAN] retries=%d, rc=%d\n", + tries, rc); + goto retry; + } + + len += sprintf(ts->boot_log + len, + "retry=%d polling time-out\n", 5-tries); + + dev_err(&client->dev, + "hello packet error.\n"); + /* Go through down*/ + + /* if iap mode enable, return and wait for IAP */ + if (ts->iap_mode == IAP_MODE_ENABLE) { + len += sprintf(ts->boot_log + len, "IAP mode enable\n"); + return rc; + } + } + + elan_dbg(client, + "__hello_packet_handler ...\n"); + + /*! - elan fw version */ + rc = __fw_packet_handler(client); + if (rc < 0) { + dev_err(&client->dev, "firmware checking error.\n"); + len += sprintf(ts->boot_log+len, "fw_packet_error=%x:%x\n", + ts->major_fw_version, ts->minor_fw_version); + + if (rc == -EINVAL) { + len += sprintf(ts->boot_log+len, + "FW version is empty, IAP enable!!\n"); + ts->protocol = ts->protocol | PRO_UPDATE_FW_MODE; + ts->iap_mode = IAP_MODE_ENABLE; + } + + /* Go through down*/ + } + + elan_dbg(client, "__fw_packet_handler...\n"); + + /*! - elan TP information */ + rc = __touch_get_resolution(client); + if (rc < 0) { + dev_err(&client->dev, + "TP information checking error.\n"); + len += sprintf(ts->boot_log+len, + "touch_get_resolution=%x:%x\n", + ts->rows, ts->cols); + /* Go through down*/ + } + + elan_dbg(client, + "__touch_get_resolution...\n"); + + + /* Get TS BootCode version */ + rc = __elan_touch_get_bc_ver(client); + if (rc < 0) { + dev_err(&client->dev, + "TP get BC version error.\n"); + len += sprintf(ts->boot_log+len, + "touch_get_BC_ver=%x:%x\n", + ts->major_bc_version, ts->major_bc_version); + /* Go through down*/ + } + + if (len == 0) + len += sprintf(ts->boot_log, "boot success!!\n"); + + return rc; + +} + + +static int elants_get_power_state(struct i2c_client *client) +{ + int rc = 0; + const uint8_t cmd[] = {CMD_R_PKT, 0x50, 0x00, 0x01}; + uint8_t buf[4], power_state; + + rc = elants_acqurie_data(client, cmd, buf, 4); + if (rc) + return rc; + + power_state = buf[1]; + elan_dbg(client, + "dump repsponse: %0x\n", power_state); + power_state = (power_state & PWR_STATE_MASK) >> 3; + elan_dbg(client, + "power state = %s\n", + power_state == PWR_STATE_DEEP_SLEEP ? + "Deep Sleep" : "Normal/Idle"); + + return power_state; +} + + +/** + * @brief \b elan_ts_recv_data - received TP data + * @param client: our i2c device + * @param buf : buffer for put received data + * + * received data from TP device. + */ +static int elants_recv_data(struct i2c_client *client, uint8_t *buf) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int rc = 0, bytes_to_recv; + + dev_dbg(&client->dev, + "[ELAN] Enter %s\n", __func__); + + if (buf == NULL) + return -EINVAL; + + mutex_lock(&ts->tr_mutex); + + bytes_to_recv = ts->rx_size; + rc = i2c_master_recv(client, buf, bytes_to_recv); + + dev_dbg(&client->dev, + "[ELAN] %d:%d:%x:%x, addr=%x\n", bytes_to_recv, rc, + buf[0], buf[34], client->addr); + dev_dbg(&client->dev, + "[ELAN] %x:%x:%x:%x:%x:%x\n", buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5]); + + if (rc != bytes_to_recv) { + dev_err(&client->dev, + "%s: i2c_master_recv error?!\n", + __func__); + rc = -EINVAL; + } + mutex_unlock(&ts->tr_mutex); + + return rc; +} + +/** + * elan_touch_pull_frame - pull a frame from the fifo + * @ed: our elan touch device + * @ehr: return buffer for the header + * @buf: data buffer + * + * Pulls a frame from the FIFO into the provided ehr and data buffer. + * The data buffer must be at least cmd_response_len bytes long. + */ +static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf) +{ + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + WARN_ON(kfifo_get(ts->fifo, buf, cmd_response_len) + != cmd_response_len); +#else + WARN_ON(kfifo_out_locked(&ts->fifo, buf, cmd_response_len, + &ts->rx_kfifo_lock) + != cmd_response_len); +#endif + return cmd_response_len; +} + + +/** + * elan_touch_fifo_clean_old - Make room for new frames + * @ed: our elan touch device + * @room: space needed + * + * Empty old frames out of the FIFO until we can fit the new one into + * the other end. + */ +static void elan_touch_fifo_clean_old(struct elants_data *ts, int room) +{ + u8 buffer[cmd_response_len]; + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + while (kfifo_len(ts->fifo) + room >= FIFO_SIZE) + elan_touch_pull_frame(ts, buffer); +#else + while (kfifo_len(&ts->fifo) + room >= FIFO_SIZE) + elan_touch_pull_frame(ts, buffer); +#endif +} + + +/** @brief \b elan_ts_GetRepoInfo - parse Multi-queue report header + * @param client : our i2c device + * @param buf : buffer data + * + * parsing report header and get data length. + * + */ +static int elants_GetRepoInfo(struct i2c_client *client, uint8_t *buf) +{ + struct elants_data *ts = i2c_get_clientdata(client); + struct multi_queue_header *buff = (struct multi_queue_header *)buf; + const u8 wait_packet[4] = {0x64, 0x64, 0x64, 0x64}; + int times = 10, rc = 0; + + switch (buf[idx_finger_header]) { + case cmd_header_byte_hello: + if (!memcmp(buf, hello_packet, 4)) + ts->wdt_reset++; + return ret_cmdrsp; + case cmd_header_byte_response: + /* Queue the data, using the fifo lock to serialize the multiple + accesses to the FIFO */ + elan_dbg(client, + "[ELAN] recv cmd_header_byte_response\n"); + + mutex_lock(&ts->fifo_mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + if (kfifo_len(ts->fifo) + cmd_response_len >= FIFO_SIZE) +#else + if (kfifo_len(&ts->fifo) + cmd_response_len >= FIFO_SIZE) + +#endif + /* Make room, make room */ + elan_touch_fifo_clean_old(ts, cmd_response_len); + /* Push the data */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + kfifo_put(ts->fifo, buf, cmd_response_len); +#else + kfifo_in_locked(&ts->fifo, buf, + cmd_response_len, &ts->rx_kfifo_lock); +#endif + mutex_unlock(&ts->fifo_mutex); + + elan_dbg(client, + "[ELAN] wake_up [%02x:%02x:%02x:%02x]\n", + buf[0], buf[1], buf[2], buf[3]); + wake_up(&ts->wait); + return ret_cmdrsp; + /* Buffer mode header */ + case queue_header_byte_normal: + elan_dbg(client, + "[ELAN] report_count=%d report_len=%d\n", + buff->report_count, buff->report_length); + if (likely(buff->report_count <= 3)) { + ts->mq_header.report_count = buff->report_count; + ts->mq_header.report_length = buff->report_length; + ts->rx_size = ts->mq_header.report_length; + } else + return ret_fail; + + break; + case queue_header_byte_wait: + dev_err(&client->dev, + "========queue_header_byte_wait %x:%x:%x:%x========\n", + buf[0], buf[1], buf[2], buf[3]); + /*! BUGFIX: buff[0] might be wrong (0x63), check buff[1 ~ 3] is enough*/ + if (!memcmp(buf+1, &wait_packet[1], 3)) { + do { + udelay(30); + elants_recv_data(client, (uint8_t *)buff); + } while (buff->packet_id != queue_header_byte_normal && + --times > 0); + if (times > 0) + rc = elants_GetRepoInfo(client, (uint8_t *)buff); + else + return ret_fail; + elan_dbg(client, + "Detect Wait_Header:rx_size=%d, report_count=%d \ + report_len=%d\n", ts->rx_size, + ts->mq_header.report_count, + ts->mq_header.report_length); + + } else + dev_err(&client->dev, + "[ELAN] ERROR!! wait header:%x:%x:%x:%x\n", + buf[0], buf[1], buf[2], buf[3]); + break; + /* Not buffer mode, it's single word mode */ + case queue_header_byte_Single: + ts->mq_header.report_count = 1; + ts->mq_header.report_length = IDX_PACKET_SIZE_WIDTH; + ts->rx_size = ts->mq_header.report_length; + return ts->rx_size; + break; + default: + dev_err(&client->dev, + "unknown multi-queue command!! --%x %x:%x:%x--\n", + buf[0], buf[1], buf[2], buf[3]); + ts->mq_header_fail++; + + /* If glitch causes frame error, drop all finger report */ + ts->rx_size = queue_packet_max_len; + elants_recv_data(client, (uint8_t *)buff); + return ret_fail; + } + + return ts->rx_size; +} + + +/** @brief \b elan_touch_checksum - Add for checksum mechanism + * @param client : our i2c device + * @param buf : buffer data + * + * caculating checksum for make sure all data validity. + * + */ +static int elan_touch_checksum(struct i2c_client *client, u8 *buf) +{ + u8 i = 0, checksum = 0; + struct elants_data *ts = i2c_get_clientdata(client); + + /*! FIXME: checksum wasn't including width byte */ + for (i = 0; i < IDX_PACKET_SIZE_WIDTH - 6; i++) + checksum = checksum + buf[i]; + + ts->checksum_correct = checksum; + + if (checksum != buf[idx_finger_width_checksum]) { + ts->checksum_fail++; + dev_err(&client->dev, + "elan touch checksum fail: %02x:%02x\n", + checksum, buf[idx_finger_width_checksum]); + return -EFAULT; + } + + return 0; +} + +/** + * @brief \b elan_touch_parse_fid - parse the 10 fid bits + * @param data : the input bit stream + * @param fid : an array of fid values + * + * Unpack the 10 bits into an array. + * + * FIXME: Review whether we can't just use << operators after making + * sure the bits are native endian ? + */ +static inline void elan_touch_parse_fid(u8 *data, u8 *fid) +{ + fid[0] = (data[0] & 0x01); + fid[1] = (data[0] & 0x02); + fid[2] = (data[0] & 0x04); + fid[3] = (data[0] & 0x08); + fid[4] = (data[0] & 0x10); + fid[5] = (data[0] & 0x20); + fid[6] = (data[0] & 0x40); + fid[7] = (data[0] & 0x80); + fid[8] = (data[1] & 0x10); + fid[9] = (data[1] & 0x20); +} + +/** + * @brief \b elan_touch_parse_wid - parse the 10 wid bits + * @param data : the input bit stream + * @param wid : an array of width level + * + * Unpack the 10 bits into an array. + * + */ +static inline void elan_touch_parse_wid(u8 *data, u8 *wid) +{ + wid[0] = (data[0] >> 4); + wid[1] = (data[0] & 0x0f); + wid[2] = (data[1] >> 4); + wid[3] = (data[1] & 0x0f); + wid[4] = (data[2] >> 4); + wid[5] = (data[2] & 0x0f); + wid[6] = (data[3] >> 4); + wid[7] = (data[3] & 0x0f); + wid[8] = (data[4] >> 4); + wid[9] = (data[4] & 0x0f); +} + + +static inline int elants_parse_xy( + uint8_t *data, + uint16_t *x, + uint16_t *y) +{ + *x = *y = 0; + + *x = (data[0] & 0xf0); + *x <<= 4; + *x |= data[1]; + + *y = (data[0] & 0x0f); + *y <<= 8; + *y |= data[2]; + + return 0; +} + +/* transition stage for lookup by table, maybe update finger report + using by Win8 50Byte format that having actual value for X, Y width */ +static inline int elan_lookup_wid( + u8 data, + u16 *w, + u16 *h) +{ + static u16 pre_w, pre_h, cur_w, cur_h; + const u8 width_lookup_table[15][2] = { + {3, 2}, {3, 2}, {6, 3}, + {6, 3}, {10, 3}, {15, 4}, + {15, 5}, {15, 5}, {15, 5}, + {15, 5}, {15, 5}, {15, 5}, + {15, 5}, {15, 5}, {15, 5}, + }; + + cur_w = width_lookup_table[data][0] * 17; + cur_h = width_lookup_table[data][1] * 17; + + /* IIR to balance w, h vaule, don't jitter too much */ + *w = (cur_w + pre_w) >> 1; + *h = (cur_h + pre_h) >> 1; + + pre_w = cur_w; + pre_h = cur_h; + + return 0; +} + +static int elan_mt_compute_slot(struct mt_device *td) +{ + int i; + for (i = 0; i < td->maxcontacts; ++i) { + if (td->slots[i].contactid == td->curdata.contactid && + td->slots[i].touch_state) + return i; + } + for (i = 0; i < td->maxcontacts; ++i) { + if (!td->slots[i].seen_in_this_frame && + !td->slots[i].touch_state) + return i; + } + /* should not occurs. If this happens that means + * that the device sent more touches that it says + * in the report descriptor. It is ignored then. */ + return -1; +} + +/* + * this function is called when a whole contact has been processed, + * so that it can assign it to a slot and store the data there + */ +static void elan_mt_complete_slot(struct mt_device *td) +{ + td->curdata.seen_in_this_frame = true; + if (td->curvalid) { + int slotnum = elan_mt_compute_slot(td); + + if (slotnum >= 0 && slotnum < td->maxcontacts) + td->slots[slotnum] = td->curdata; + } + td->num_received++; +} + +/* + * this function is called when a whole packet has been received and processed, + * so that it can decide what to send to the input layer. + */ + +#if (MT_TYPE_B == 0) +static void elan_mt_emit_event( + struct mt_device *td, + struct input_dev *input) +{ + struct elants_data *ts = + container_of(td, struct elants_data, td); + int i; + + for (i = 0; i < td->maxcontacts; ++i) { + struct mt_slot *s = &(td->slots[i]); + if (!s->seen_in_this_frame) { + if (s->touch_state) { + s->touch_state = false; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + /* ingore finger-up event since TF1 seems having problem */ + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->touch_state); + input_mt_sync(input); +#endif + } + s->seen_in_this_frame = false; + continue; + } + + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->touch_state); + + if (s->touch_state) { + /* this finger is on the screen */ + int wide = (s->w > s->h); + /* divided by two to match visual scale of touch */ + int major = max(s->w, s->h) >> 1; + int minor = min(s->w, s->h) >> 1; + + elan_dbg(ts->client, + "[ELAN] i=%d x=%x y=%x w=%x h=%x, p=%x\n", + i, s->x, s->y, major, minor, s->p); + + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, s->contactid); + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); +#endif + input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, major); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + /* Can't not report on Android 2.2, 10th finger will disorder */ + input_event(input, EV_ABS, ABS_MT_WIDTH_MINOR, minor); +#endif + } + input_mt_sync(input); + s->seen_in_this_frame = false; + } + + input_sync(input); + td->num_received = 0; +} +#else /* For Android ICS, input protocol-B */ +static void elan_mt_emit_event( + struct mt_device *td, + struct input_dev *input) +{ + struct elants_data *ts = + container_of(td, struct elants_data, td); + int i; + + for (i = 0; i < td->maxcontacts; ++i) { + struct mt_slot *s = &(td->slots[i]); + if (!s->seen_in_this_frame) + s->touch_state = false; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, s->touch_state); + if (s->touch_state) { + /* this finger is on the screen */ + int wide = (s->w > s->h); + /* divided by two to match visual scale of touch */ + int major = max(s->w, s->h) >> 1; + int minor = min(s->w, s->h) >> 1; + + elan_dbg(ts->client, + "[ELAN] i=%d x=%x y=%x w=%x h=%x\n", + i, s->x, s->y, major, minor); + + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + } + s->seen_in_this_frame = false; + } + + input_mt_report_pointer_emulation(input, true); + input_sync(input); + td->num_received = 0; +} + +#endif + + +/** @brief \b elan_mt_event - process finger reports + * @param ts: our touchscreen + * @param finger_stat : number of fingers in packet + * @param buf : received buffer + * + * Walk the received report and process the finger data, extracting + * and reporting co-ordinates. No locking is needed here as the workqueue + * does our threading for us. + */ +static int elan_mt_event( + struct elants_data *ts, + int finger_stat, + u8 *buf) +{ + int i; + u8 fid[FINGER_NUM], wid[FINGER_NUM]; + u16 x, y, w, h; + struct i2c_client *client = ts->client; + struct mt_device *td = &ts->td; + + dev_dbg(&client->dev, + "[ELAN] Enter elan_mt_event Func\n"); + + /* Parsing Finger, Width field */ + elan_touch_parse_fid(&buf[idx_finger_state], &fid[0]); + elan_touch_parse_wid(&buf[idx_finger_width], &wid[0]); + + /* number of fingers expects in this frame */ + td->num_expected = finger_stat; + for (i = 0; i < td->maxcontacts; i++) { + if (finger_stat == 0) + break; + + /* tracking id */ + td->curdata.contactid = (fid[i] > 0) ? i+1 : 0; + + if (td->curdata.contactid == 0) + continue; + + td->curdata.touch_state = true; + + elants_parse_xy(&buf[3+i*3], &x, &y); + td->curdata.x = x; + td->curdata.y = y; + + elan_lookup_wid(wid[i], &w, &h); + td->curdata.w = w; + td->curdata.h = h; + + finger_stat--; + + elan_mt_complete_slot(td); + } + + if (td->num_received >= td->num_expected) + elan_mt_emit_event(td, ts->input); + + return 1; +} + + + +/** @brief \b elants_report_data - report finger report to user space. + * @param client : our i2c device + * @param buf : raw data from TP device. + * + * - reporting finger data to user space. + * - packet_fail count + * + */ +static void elants_report_data(struct i2c_client *client, uint8_t *buf) +{ + struct elants_data *ts = i2c_get_clientdata(client); + + switch (buf[idx_finger_header]) { + case idx_coordinate_packet_4_finger: + case idx_coordinate_packet_10_finger: { + u8 finger_stat = buf[idx_finger_total] & 0x0f; + elan_dbg(client, + "[ELAN] finger_stat == %d\n", finger_stat); + elan_dbg(client, + "[ELAN] finger:%x:%x:%x:%x:%x\n", + buf[0], buf[1], buf[2], buf[3], buf[4]); + + /* Enter right process, reset int_status*/ + ts->polling.int_status = 0; + ts->packet_received++; + + if (likely(finger_stat != 0)) { + ts->td.curvalid = true; + /* Old way for report input event + elan_touch_report_fingers(ts, finger_stat, buf); */ + elan_mt_event(ts, finger_stat, buf); + ts->touched_sync++; + } else { + input_mt_sync(ts->input); + ts->no_touched_sync++; + input_sync(ts->input); + } + } + break; + default: + ts->header_fail++; + dev_err(&client->dev, + "[ELAN] %s: unknown packet type: %x:%x:%x:%x\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + break; + } + + return; +} + +/** @brief \b elants_drop_packet - err handler that drop all packet. + * + * mutex protection will at outside this function. + * + */ +static void elants_drop_packet(struct elants_data *ts) +{ + uint8_t buf[queue_packet_max_len] = {0x0}; + + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return; + + if (gpio_get_value(ts->intr_gpio) != 0) + goto end; + + /* -# Read multi_queue header */ + ts->rx_size = queue_header_size; + if (elants_recv_data(ts->client, buf) < 0) + goto end; + + /* -# Get multi_queue header info*/ + if (elants_GetRepoInfo(ts->client, buf) > 0) { + /* 3. Get finger report data */ + elants_recv_data(ts->client, buf); + udelay(10); + if (gpio_get_value(ts->intr_gpio) != 0) + goto end; + } + + /* -#received all packet till ts->intr pull high */ + ts->rx_size = 1; + while (gpio_get_value(ts->intr_gpio) == 0) { + elants_recv_data(ts->client, buf); + udelay(10); + } + +end: + ts->rx_size = queue_header_size; + dev_dbg(&ts->client->dev, + "[ELAN] Leave %s\n", __func__); + + return; +} + + + +/** @brief \b elan_ts_pollWork_func - regular processing + * @param work : pass from kernel + * + * poll_timer polled processing to occur elan_ts_pollWork_func() to check if our ts + * is abnormal. + * + */ +static void elants_pollWork_func(struct work_struct *work) + +{ + struct elants_data *ts = + container_of(work, struct elants_data, pollingwork); + + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + goto fail; + + if (gpio_get_value(ts->intr_gpio) != 0) + goto fail; + else { + ts->polling.int_status++; + if (ts->polling.int_status < 3) + goto fail; + } + + /* - we use mutex_trylock() here since polling is not very important */ + if (!mutex_trylock(&ts->mutex)) { + elan_dbg(ts->client, + "[ELAN] trylock fail! return...\n"); + goto fail; + } + + if (test_bit(idx_finger_report, &ts->busy)) { + elan_dbg(ts->client, + "[ELAN] 1.finger_Report processing ... ignore!!\n"); + goto unlock; + } + + if (test_bit(idx_cmd_handshake, &ts->busy)) { + elan_dbg(ts->client, + "[ELAN] 2.command processing ... ignore!!\n"); + goto unlock; + } + + dev_err(&ts->client->dev, + "[ELAN] Force to release intr_gpio!!\n"); + + ts->drop_frame++; + elants_drop_packet(ts); + +unlock: + mutex_unlock(&ts->mutex); + +fail: + return; +} + +static irqreturn_t elants_work_func(int irq, void *work) +{ + struct elants_data *ts = work; + uint8_t buf[queue_packet_max_len] = {0x0}; + u8 pos = 0, rc; + + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + if (ts->protocol & PRO_UPDATE_FW_MODE) + return IRQ_HANDLED; + + /* - this means that we have already serviced it or glich happen!! */ + if (gpio_get_value(ts->intr_gpio) != 0) { + dev_dbg(&ts->client->dev, + "[ELAN] Leave %s(gpio)\n", __func__); + return IRQ_HANDLED; + } + + mutex_lock(&ts->mutex); + + set_bit(idx_finger_report, &ts->busy); + + /* Report one setting */ + /* ts->rx_size = 40; */ /* queue_header_size; */ + /* ts->mq_header.report_count=1; */ + + + /* - Read multi_queue header */ + if (elants_recv_data(ts->client, buf) < 0) + goto fail; + + /* - Get multi_queue header info */ + rc = elants_GetRepoInfo(ts->client, buf); + if (rc < 0 || rc == ret_cmdrsp) + goto fail; + + /* - Get finger report data */ + if (elants_recv_data(ts->client, buf) < 0) + goto fail; + + clear_bit(idx_finger_report, &ts->busy); + + mutex_unlock(&ts->mutex); + + /* - parsing report and send out */ + while (ts->mq_header.report_count--) { + if (elan_touch_checksum(ts->client, buf + pos) == 0) + elants_report_data(ts->client, buf + pos); + pos = pos + IDX_PACKET_SIZE_WIDTH; + udelay(10); + } + + ts->rx_size = queue_header_size; + dev_dbg(&ts->client->dev, + "[ELAN] Leave %s\n", __func__); + return IRQ_HANDLED; + +fail: + clear_bit(idx_finger_report, &ts->busy); + mutex_unlock(&ts->mutex); + ts->rx_size = queue_header_size; + + dev_dbg(&ts->client->dev, + "[ELAN] Leave %s(Fail)\n", __func__); + return IRQ_HANDLED; +} + + + +/** + * @brief \b elan_touch_timer_func - poll processing + * @param timer : our timer + * + * Queue polled processing to occur on our touch panel and kick the timer + * off again + * + * CHECK: we have no guarantee that the timer will not run multiple times + * within one poll, does it matter ? + */ +static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer) +{ + struct elants_data *ts = container_of(timer, struct elants_data, timer); + queue_work(ts->elan_wq, &ts->work); + hrtimer_start(&ts->timer, + ktime_set(0, 12500000), HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} + +/** + * @brief \b elants_poll_timer_func - err handler when intr_gpio is low but no isr serviced it. + * @param data : our ts + * + * intr_gpio polling checking, it'll force to get data if intr_gpio is low + * and not in isr routine. + * + */ +static void elants_poll_timer_func(unsigned long data) +{ + struct elants_data *ts = (struct elants_data *)data; + struct elan_polling *timer = &ts->polling; + + dev_dbg(&ts->client->dev, + "[ELAN] Enter %s\n", __func__); + + /* - ignore it if normal work is processing */ + if (work_pending(&ts->work)) { + dev_dbg(&ts->client->dev, + "[ELAN] 1.work_pending ... ignore!!\n"); + goto reset; + } + + /* - ignore it if poll_timer work is processing */ + if (work_pending(&ts->pollingwork)) { + dev_dbg(&ts->client->dev, + "[ELAN] 2.work_pending ... ignore!!\n"); + goto reset; + } + + if (!queue_work(timer->elan_wq, &ts->pollingwork)) + dev_err(&ts->client->dev, + "[ELAN] pollWork active failed!!\n"); + +reset: + timer->timer.expires = jiffies + 10 * HZ; + add_timer(&ts->polling.timer); + + return; +} + +static irqreturn_t elants_irq_handler(int irq, void *dev_id) +{ + struct elants_data *ts = dev_id; + struct i2c_client *client = ts->client; + + dev_dbg(&client->dev, + "[ELAN] %s\n", __func__); + + ts->irq_received++; + + return IRQ_WAKE_THREAD; +} + + +static int elants_register_interrupt(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + int err = 0; + + if (client->irq) { + + ts->use_irq = 1; + + err = request_threaded_irq(client->irq, elants_irq_handler, + elants_work_func, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, ts); + if (err) + dev_err(&client->dev, + "%s: request_irq %d failed\n", + __func__, client->irq); + + ts->status |= STA_USE_IRQ; + + elan_dbg(client, + "[ELAN] %s in interrupt mode\n", ts->input->name); + } + + if (!ts->use_irq) { + hrtimer_init(&ts->timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = elan_touch_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), + HRTIMER_MODE_REL); + elan_dbg(client, + "[ELAN] %s in time-polling mode\n", ts->input->name); + } + + return err; +} + +static int remove_elants(struct i2c_client *client) +{ + int ret = 0; + struct elants_data *ts = i2c_get_clientdata(client); + + if (&client->dev.kobj) + sysfs_remove_group(&client->dev.kobj, &elan_attribute_group); + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (&ts->early_suspend) + unregister_early_suspend(&ts->early_suspend); +#endif + + if (ts->use_irq) { + if (client->irq) + free_irq(client->irq, ts); + } else { + if (&ts->timer) + hrtimer_cancel(&ts->timer); + } + +#if (MT_TYPE_B) + input_mt_destroy_slots(ts->input); +#endif + + if (ts->input) + input_unregister_device(ts->input); + + if (ts->elan_wq) + destroy_workqueue(ts->elan_wq); + + if (ts->polling.elan_wq) + destroy_workqueue(ts->polling.elan_wq); + + if (&ts->polling.timer) { + ret = del_timer(&ts->polling.timer); + if (ret != 0) + dev_err(&client->dev, + "[ELAN] del_timer fail!!\n"); + } + + if (&ts->mutex) + mutex_destroy(&ts->mutex); + if (&ts->tr_mutex) + mutex_destroy(&ts->tr_mutex); + if (&ts->fifo_mutex) + mutex_destroy(&ts->fifo_mutex); + + if (ts->fw_enabled) { + ret = misc_deregister(&ts->firmware); + if (ret < 0) + dev_err(&client->dev, + "[ELAN] misc_deregister fail!!\n"); + } + + kfree(ts->td.slots); + + /* free kfifo */ + kfifo_free(&ts->fifo); +#if (ANDROID) + wake_lock_destroy(&ts->wakelock); +#endif + + kfree(ts); + + return ret; +} + + +/** + * probe_thread_func - init touch device. + * @work: /int work queue + * + * Perform real probe for our I2C device and if successful configure + * it up as an input device. If not then clean up and return an error + * code. + */ + +static int probe_thread_func(void *data) +{ + struct elants_data *ts = data; + struct i2c_client *client = ts->client; + int err = 0; + + mutex_lock(&ts->mutex); + + err = elants_setup(client); + if (err < 0) { + dev_err(&client->dev, + "[ELAN] %s probe failed\n", + ELAN_DEV_NAME); + /* User can IAP anyway, continue */ + } + + set_bit(BTN_TOUCH, ts->input->keybit); + + /*! - Single touch input params setup */ + input_set_abs_params(ts->input, ABS_X, 0, ts->cols, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, ts->rows, 0, 0); + input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0); + + /*! @warning FIXME: ABS_MT_PRESSURE can be supported by FW. + currently we have done it by ABS_MT_WIDTH_MAJOR */ + /*! - Multitouch input params setup */ + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->cols, 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->rows, 0, 0); +#if (MT_TYPE_B == 0) + input_set_abs_params(ts->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0); + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 1, 0, 0); + + input_set_abs_params(ts->input, ABS_MT_TRACKING_ID, 0, FINGER_NUM, 0, 0); +#else + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + + /* not test, keep mark for less Android-ICS */ + input_set_abs_params(ts->input, ABS_MT_ORIENTATION, 0, 1, 0, 0); +#endif + + __set_bit(EV_ABS, ts->input->evbit); + __set_bit(EV_SYN, ts->input->evbit); + + input_set_drvdata(ts->input, ts); + + err = input_register_device(ts->input); + if (err) { + dev_err(&client->dev, + "[ELAN] %s unable to register input device\n", + ELAN_DEV_NAME); + goto fail_un; + } + + ts->td.slots = kzalloc(FINGER_NUM * sizeof(struct mt_slot), + GFP_KERNEL); + if (!ts->td.slots) { + dev_err(&client->dev, + "cannot allocate multitouch slots\n"); + goto fail_un; + } + ts->td.maxcontacts = FINGER_NUM; +#if (MT_TYPE_B) + input_mt_init_slots(ts->input, ts->td.maxcontacts); +#endif + + /*! @warning If the firmware device fails we carry on as it doesn't stop normal + usage */ + private_ts = &ts->firmware; + if (elants_register_interrupt(ts->client) < 0) { + dev_err(&client->dev, + "[ELAN] %s register_interrupt failed!\n", + ELAN_DEV_NAME); + goto fail_un; + } + + /*! - Register SW watchdog timer */ + init_timer(&ts->polling.timer); + /*! - check intr_gpio every 10secs */ + ts->polling.timer.expires = jiffies + 10 * HZ; + ts->polling.timer.function = &elants_poll_timer_func; + ts->polling.timer.data = (unsigned long)ts; + add_timer(&ts->polling.timer); + + mutex_unlock(&ts->mutex); + + ts->status |= STA_INIT4; + + return 0; + +fail_un: + mutex_unlock(&ts->mutex); + remove_elants(client); + return err; +} + + + +/** + * @brief elants_probe - probe for touchpad + * @param client : our I2C client + * + * Perform setup and probe for our I2C device and if successful configure + * it up as an input device. If not then clean up and return an error + * code. + */ +static int __devinit elants_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + long err = -1; + struct elan_i2c_platform_data *pdata = NULL; + struct elants_data *ts = + kzalloc(sizeof(struct elants_data), GFP_KERNEL); + + if (ts == NULL) + return -ENOMEM; + + ts->status |= STA_PROBED; + + mutex_init(&ts->mutex); + mutex_init(&ts->tr_mutex); + mutex_init(&ts->fifo_mutex); + init_waitqueue_head(&ts->wait); + spin_lock_init(&ts->rx_kfifo_lock); +#if (ANDROID) + wake_lock_init(&ts->wakelock, WAKE_LOCK_SUSPEND, "elan_touch"); +#endif + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "[ELAN] %s: i2c check functionality error\n", + ELAN_DEV_NAME); + goto fail_un; + } + + /* @{ - Normal operatin (Finger report) */ + ts->elan_wq = create_singlethread_workqueue("elan_wq"); + if (!ts->elan_wq) { + dev_err(&client->dev, + "[ELAN] %s: create workqueue failed\n", + ELAN_DEV_NAME); + goto fail_un; + } + + /*! - Regular polling Process */ + ts->polling.elan_wq = create_singlethread_workqueue("elan_poll_wq"); + if (!ts->polling.elan_wq) { + dev_err(&client->dev, + "[ELAN] %s: create workqueue failed\n", + ELAN_DEV_NAME); + goto fail_un; + } + + /* INIT polling machinams */ + INIT_WORK(&ts->pollingwork, elants_pollWork_func); + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, + "[ELAN] %s no platform data\n", + ELAN_DEV_NAME); + goto fail_un; + } + + ts->status &= STA_INIT; + + ts->intr_gpio = pdata->intr_gpio; + ts->rst_gpio = pdata->rst_gpio; + /* set initial i2c address */ + client->addr = elan_i2c_master; + ts->i2caddr = client->addr; + + elan_dbg(client, + "reset=%d, intr=%d\n", ts->rst_gpio, ts->intr_gpio); + + ts->client = client; + i2c_set_clientdata(client, ts); + + /* INIT kfifo */ + err = kfifo_alloc(&ts->fifo, FIFO_SIZE, GFP_KERNEL); + if (!kfifo_initialized(&ts->fifo)) { + dev_err(&client->dev, + "%s error kfifo_alloc\n", __func__); + goto fail_un; + } + + + /*! - IAP Device Initial */ + ts->firmware.minor = MISC_DYNAMIC_MINOR; + ts->firmware.name = "elan-iap"; + ts->firmware.fops = &elan_touch_fops; + ts->firmware.mode = S_IRWXUGO; + + if (unlikely(misc_register(&ts->firmware) < 0)) + dev_err(&client->dev, + "[ELAN] IAP device register failed!!"); + else { + elan_dbg(client, + "[ELAN] IAP device register finished!!"); + ts->fw_enabled = 1; + } + + ts->status &= STA_INIT2; + + ts->input = input_allocate_device(); + if (ts->input == NULL) { + dev_err(&client->dev, + "[ELAN] %s Failed to allocate input device\n", + ELAN_DEV_NAME); + goto fail_un; + } + + ts->input->name = ELAN_DEV_NAME; + ts->input->open = elan_open; + ts->input->close = elan_close; + + /* Says HELLO to touch device */ + ts->thread = kthread_run(probe_thread_func, ts, client->name); + if (IS_ERR(ts->thread)) { + err = PTR_ERR(ts->thread); + goto fail_un; + } + + ts->status &= STA_INIT3; + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING - 1; + ts->early_suspend.suspend = elants_early_suspend; + ts->early_suspend.resume = elants_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + /* - register sysfs @} */ + elan_dbg(client, + "[ELAN] create sysfs!!\n"); + if (sysfs_create_group(&client->dev.kobj, &elan_attribute_group)) + dev_err(&client->dev, + "[ELAN] sysfs create group error\n"); + + + return 0; + +fail_un: + remove_elants(client); + return err; +} + +static int __devexit elants_remove(struct i2c_client *client) +{ + return remove_elants(client); +} + +#ifdef CONFIG_PM_SLEEP +/** @brief \b elan_ts_suspend - enter sleep mode + * + * when Suspend mode, disable_irq and cancel work queue. + * + */ +static int elants_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct elants_data *ts = i2c_get_clientdata(client); + const u8 set_sleep_cmd[] = {0x54, 0x50, 0x00, 0x01}; + int rc = 0; + + dev_err(&client->dev, + "[ELAN] %s: enter\n", __func__); + + /* Command not support in IAP recovery mode */ + if (ts->protocol & PRO_UPDATE_FW_MODE) + return 0; + + disable_irq(client->irq); + cancel_work_sync(&ts->work); + + mutex_lock(&ts->mutex); + rc = elants_set_data(client, + set_sleep_cmd, sizeof(set_sleep_cmd)); + + if (rc < 0) + goto end; + + ts->power_state = 0; + /* de-active timer */ + del_timer_sync(&ts->polling.timer); + + +end: + mutex_unlock(&ts->mutex); + return rc; +} + + +/** @brief \b elan_ts_resume - enter wake-up mode + * + * when Wake-up mode, enable_irq. + * + */ +static int elants_resume(struct i2c_client *client) +{ + struct elants_data *ts = i2c_get_clientdata(client); + const u8 set_active_cmd[] = {0x54, 0x58, 0x00, 0x01}; + int rc = 0, retry = 5; + + dev_err(&client->dev, + "[ELAN] %s: enter\n", + __func__); + + /* Command not support in IAP recovery mode */ + if (ts->protocol & PRO_UPDATE_FW_MODE) + return 0; + + mutex_lock(&ts->mutex); + + do { + rc = elants_set_data(client, + set_active_cmd, sizeof(set_active_cmd)); + elan_msleep(2); + rc = elants_get_power_state(client); + if (unlikely(rc != PWR_STATE_NORMAL)) + dev_err(&client->dev, + "[ELAN] %s: wake up tp failed! err = %d\n", + __func__, rc); + else + break; + } while (--retry); + + ts->power_state = 1; + mutex_unlock(&ts->mutex); + + /* re-active poll timer */ + mod_timer(&ts->polling.timer, jiffies + 10 * HZ); + + enable_irq(client->irq); + + return rc; +} +#endif + + +#ifdef CONFIG_HAS_EARLYSUSPEND +/** @brief \b elants_early_suspend - enter sleep mode + * + * This function is called by kernel, + * elan_ts_early_suspend() -> elan_ts_suspend() + * + */ +static void elants_early_suspend(struct early_suspend *h) +{ + struct elants_data *ts = + container_of(h, struct elants_data, early_suspend); + dev_dbg(&ts->client->dev, + "Enter %s\n", __func__); + + elants_suspend(ts->client, PMSG_SUSPEND); + return; +} + +/** @brief \b elants_late_resume - enter wake-up mode + * + * This function is called by kernel, + * elan_ts_late_resume() -> elan_ts_resume() + * + */ +static void elants_late_resume(struct early_suspend *h) +{ + struct elants_data *ts = + container_of(h, struct elants_data, early_suspend); + dev_dbg(&ts->client->dev, + "Enter %s\n", __func__); + + elants_resume(ts->client); + return; +} +#endif + +/*! brief system registeration */ +static const struct i2c_device_id elan_ts_id[] = { + { ELAN_DEV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, elan_ts_id); + +/*! brief system registeration */ +static struct i2c_driver elan_ts_driver = { + .probe = elants_probe, + .remove = __devexit_p(elants_remove), +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = elants_suspend, + .resume = elants_resume, +#endif + .id_table = elan_ts_id, + .driver = { + .name = ELAN_DEV_NAME, + .owner = THIS_MODULE, + .bus = &i2c_bus_type, + }, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) +static int __devinit elan_ts_init(void) +{ + return i2c_add_driver(&elan_ts_driver); +} + +static void __exit elan_ts_exit(void) +{ + i2c_del_driver(&elan_ts_driver); + return; +} +module_init(elan_ts_init); +module_exit(elan_ts_exit); +#else +module_i2c_driver(elan_ts_driver); +#endif + +MODULE_VERSION(DRIVER_VERSION); +MODULE_DESCRIPTION("Elan TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/elants.h b/include/linux/i2c/elants.h new file mode 100644 index 0000000..5b45a0c --- /dev/null +++ b/include/linux/i2c/elants.h @@ -0,0 +1,61 @@ +#ifndef _LINUX_I2C_ELANTS_H +#define _LINUX_I2C_ELANTS_H + +#define ELAN_DEV_NAME "elants_i2c" + +/* set TSP resolution by actual TPM */ +#define ELAN_X_MAX 2944 +#define ELAN_Y_MAX 1856 + + +/*! @brief \b platform data + * @param intr_gpio : gpio pin + * @param rst_gpio : interuption pin + * + * Platform data, all platform dependent variable should put here. + * + */ +struct elan_i2c_platform_data { + unsigned short version; + unsigned int abs_x_min; + unsigned int abs_x_max; + unsigned int abs_y_min; + unsigned int abs_y_max; + int intr_gpio; + int rst_gpio; +}; + +struct elan_ioctl_data { + int len; + char buf[32]; +}; + +/*! @brief \b ioctl command definition. + * @param IOCTL_I2C_SLAVE : set i2c address that would be controled right now. + * @param IOCTL_MAJOR_FW_VER : fw major number + * @param IOCTL_MINOR_FW_VER : fw minor number + * @param IOCTL_RESET : Hardware Reset + * @param IOCTL_SET_COMMAND : control command set to TP device. + * @param IOCTL_GET_COMMAND : get response from our TP device. + * @param IOCTL_IAP_ENABLE : Enter IAP mode + * @param IOCTL_IAP_DISABLE : Leave IAP mode + * @param IOCTL_I2C_INT : gpio status + * + * + * ioctl command, easy user to control our TP from application. + * + */ +#define ELAN_IOCTLID 0xD0 +#define IOCTL_I2C_SLAVE _IOW(ELAN_IOCTLID, 1, int) +#define IOCTL_MAJOR_FW_VER _IOR(ELAN_IOCTLID, 2, int) +#define IOCTL_MINOR_FW_VER _IOR(ELAN_IOCTLID, 3, int) +#define IOCTL_RESET _IOW(ELAN_IOCTLID, 4, int) +#define IOCTL_I2C_INT _IOR(ELAN_IOCTLID, 8, int) +#define IOCTL_SET_COMMAND _IOW(ELAN_IOCTLID, 9, unsigned long) +#define IOCTL_GET_COMMAND _IOR(ELAN_IOCTLID, 10, unsigned long) +#define IOCTL_IAP_ENABLE _IOW(ELAN_IOCTLID, 11, int) +#define IOCTL_IAP_DISABLE _IOW(ELAN_IOCTLID, 12, int) +#define IOCTL_CHECK_RECOVERY_MODE _IOR(ELAN_IOCTLID, 13, int) +#define IOCTL_BOOTCODE_CMD _IOW(ELAN_IOCTLID, 14, unsigned long) + +#endif /* _LINUX_I2C_ELANTS_H */
This patch is for Elan eKTF Touchscreen product, I2C adpater module. This driver adds communication support with Elan eKTF controller using I2C bus. Signed-off-by: Scott Liu <scott.liu@emc.com.tw> --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/elants_i2c.c | 3182 ++++++++++++++++++++++++++++++++ include/linux/i2c/elants.h | 61 + 4 files changed, 3253 insertions(+) create mode 100644 drivers/input/touchscreen/elants_i2c.c create mode 100644 include/linux/i2c/elants.h