Re: [PATCH v15 02/12] input: cyapa: add gen5 trackpad device basic functions support

From: Jeremiah Mahler
Date: Mon Dec 15 2014 - 09:11:06 EST


Dudley,

On Mon, Dec 15, 2014 at 02:23:13PM +0800, Dudley Du wrote:
> Based on the cyapa core, add the gen5 trackpad device's basic functions
> supported, so gen5 trackpad device can work with kernel input system.
> And also based on the state parse interface, the cyapa driver can
> automatically determine the attached is gen3 or gen5 protocol trackpad
> device, then set the correct protocol to work with the attached
> trackpad device.
> TEST=test on Chromebooks.
>
> Signed-off-by: Dudley Du <dudley.dulixin@xxxxxxxxx>
> ---
> drivers/input/mouse/Makefile | 2 +-
> drivers/input/mouse/cyapa.c | 13 +
> drivers/input/mouse/cyapa.h | 1 +
> drivers/input/mouse/cyapa_gen5.c | 1660 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1675 insertions(+), 1 deletion(-)
> create mode 100644 drivers/input/mouse/cyapa_gen5.c
>
> diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
> index 8bd950d..8a9c98e 100644
> --- a/drivers/input/mouse/Makefile
> +++ b/drivers/input/mouse/Makefile
> @@ -24,7 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
> obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
> obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
>
> -cyapatp-objs := cyapa.o cyapa_gen3.o
> +cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
> psmouse-objs := psmouse-base.o synaptics.o focaltech.o
>
> psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
> diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
> index ae1df15..d4560a3 100644
> --- a/drivers/input/mouse/cyapa.c
> +++ b/drivers/input/mouse/cyapa.c
> @@ -157,6 +157,14 @@ static int cyapa_get_state(struct cyapa *cyapa)
> if (!error)
> goto out_detected;
> }
> + if ((cyapa->gen == CYAPA_GEN_UNKNOWN ||
> + cyapa->gen == CYAPA_GEN5) &&
> + !smbus && even_addr) {
> + error = cyapa_gen5_ops.state_parse(cyapa,
> + status, BL_STATUS_SIZE);
> + if (!error)
> + goto out_detected;
> + }
>
> /*
> * Write 0x00 0x00 to trackpad device to force update its
> @@ -240,6 +248,9 @@ static int cyapa_check_is_operational(struct cyapa *cyapa)
> return error;
>
> switch (cyapa->gen) {
> + case CYAPA_GEN5:
> + cyapa->ops = &cyapa_gen5_ops;
> + break;
> case CYAPA_GEN3:
> cyapa->ops = &cyapa_gen3_ops;
> break;
> @@ -476,6 +487,8 @@ static int cyapa_initialize(struct cyapa *cyapa)
>
> if (cyapa_gen3_ops.initialize)
> error = cyapa_gen3_ops.initialize(cyapa);
> + if (!error && cyapa_gen5_ops.initialize)
> + error = cyapa_gen5_ops.initialize(cyapa);
> if (error)
> return error;
>
> diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
> index 709ad47..d7d4f11 100644
> --- a/drivers/input/mouse/cyapa.h
> +++ b/drivers/input/mouse/cyapa.h
> @@ -312,5 +312,6 @@ u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
>
> extern const char product_id[];
> extern const struct cyapa_dev_ops cyapa_gen3_ops;
> +extern const struct cyapa_dev_ops cyapa_gen5_ops;
>
> #endif
> diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
> new file mode 100644
> index 0000000..1ac264d
> --- /dev/null
> +++ b/drivers/input/mouse/cyapa_gen5.c
> @@ -0,0 +1,1660 @@
> +/*
> + * Cypress APA trackpad with I2C interface
> + *
> + * Author: Dudley Du <dudl@xxxxxxxxxxx>
> + *
> + * Copyright (C) 2014 Cypress Semiconductor, Inc.
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/slab.h>
> +#include <linux/unaligned/access_ok.h>
> +#include "cyapa.h"
> +
> +
> +/* Macro of Gen5 */
> +#define RECORD_EVENT_NONE 0
> +#define RECORD_EVENT_TOUCHDOWN 1
> +#define RECORD_EVENT_DISPLACE 2
> +#define RECORD_EVENT_LIFTOFF 3
> +
> +#define CYAPA_TSG_FLASH_MAP_BLOCK_SIZE 0x80
> +#define CYAPA_TSG_IMG_FW_HDR_SIZE 13
> +#define CYAPA_TSG_FW_ROW_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE)
> +#define CYAPA_TSG_IMG_START_ROW_NUM 0x002e
> +#define CYAPA_TSG_IMG_END_ROW_NUM 0x01fe
> +#define CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM 0x01ff
> +#define CYAPA_TSG_IMG_MAX_RECORDS (CYAPA_TSG_IMG_END_ROW_NUM - \
> + CYAPA_TSG_IMG_START_ROW_NUM + 1 + 1)
> +#define CYAPA_TSG_IMG_READ_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE / 2)
> +#define CYAPA_TSG_START_OF_APPLICATION 0x1700
> +#define CYAPA_TSG_APP_INTEGRITY_SIZE 60
> +#define CYAPA_TSG_FLASH_MAP_METADATA_SIZE 60
> +#define CYAPA_TSG_BL_KEY_SIZE 8
> +
> +/* Macro definitions for Gen5 trackpad device. */
> +#define GEN5_TOUCH_REPORT_HEAD_SIZE 7
> +#define GEN5_TOUCH_REPORT_MAX_SIZE 127
> +#define GEN5_BTN_REPORT_HEAD_SIZE 6
> +#define GEN5_BTN_REPORT_MAX_SIZE 14
> +#define GEN5_WAKEUP_EVENT_SIZE 4
> +#define GEN5_RAW_DATA_HEAD_SIZE 24
> +
> +#define GEN5_BL_CMD_REPORT_ID 0x40
> +#define GEN5_BL_RESP_REPORT_ID 0x30
> +#define GEN5_APP_CMD_REPORT_ID 0x2f
> +#define GEN5_APP_RESP_REPORT_ID 0x1f
> +
> +#define GEN5_APP_DEEP_SLEEP_REPORT_ID 0xf0
> +#define GEN5_DEEP_SLEEP_RESP_LENGTH 5
> +
> +#define GEN5_PARAMETER_ACT_INTERVL_ID 0x4d
> +#define GEN5_PARAMETER_ACT_INTERVL_SIZE 1
> +#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID 0x4f
> +#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE 2
> +#define GEN5_PARAMETER_LP_INTRVL_ID 0x4c
> +#define GEN5_PARAMETER_LP_INTRVL_SIZE 2
> +
> +#define GEN5_PARAMETER_DISABLE_PIP_REPORT 0x08
> +
> +#define GEN5_POWER_STATE_ACTIVE 0x01
> +#define GEN5_POWER_STATE_LOOK_FOR_TOUCH 0x02
> +#define GEN5_POWER_STATE_READY 0x03
> +#define GEN5_POWER_STATE_IDLE 0x04
> +#define GEN5_POWER_STATE_BTN_ONLY 0x05
> +#define GEN5_POWER_STATE_OFF 0x06
> +
> +#define GEN5_DEEP_SLEEP_STATE_MASK 0x03
> +#define GEN5_DEEP_SLEEP_STATE_ON 0x00
> +#define GEN5_DEEP_SLEEP_STATE_OFF 0x01
> +
> +#define GEN5_DEEP_SLEEP_OPCODE 0x08
> +#define GEN5_DEEP_SLEEP_OPCODE_MASK 0x0f
> +
> +#define GEN5_POWER_READY_MAX_INTRVL_TIME 50 /* Unit: ms */
> +#define GEN5_POWER_IDLE_MAX_INTRVL_TIME 250 /* Unit: ms */
> +
> +#define GEN5_CMD_REPORT_ID_OFFSET 4
> +
> +#define GEN5_RESP_REPORT_ID_OFFSET 2
> +#define GEN5_RESP_RSVD_OFFSET 3
> +#define GEN5_RESP_RSVD_KEY 0x00
> +#define GEN5_RESP_BL_SOP_OFFSET 4
> +#define GEN5_SOP_KEY 0x01 /* Start of Packet */
> +#define GEN5_EOP_KEY 0x17 /* End of Packet */
> +#define GEN5_RESP_APP_CMD_OFFSET 4
> +#define GET_GEN5_CMD_CODE(reg) ((reg) & 0x7f)
> +
> +#define VALID_CMD_RESP_HEADER(resp, cmd) \
> + (((resp)[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID) && \
> + ((resp)[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) && \
> + (GET_GEN5_CMD_CODE((resp)[GEN5_RESP_APP_CMD_OFFSET]) == (cmd)))
> +
> +#define GEN5_MIN_BL_CMD_LENGTH 13
> +#define GEN5_MIN_BL_RESP_LENGTH 11
> +#define GEN5_MIN_APP_CMD_LENGTH 7
> +#define GEN5_MIN_APP_RESP_LENGTH 5
> +#define GEN5_UNSUPPORTED_CMD_RESP_LENGTH 6
> +
> +#define GEN5_RESP_LENGTH_OFFSET 0x00
> +#define GEN5_RESP_LENGTH_SIZE 2
> +
> +#define GEN5_HID_DESCRIPTOR_SIZE 32
> +#define GEN5_BL_HID_REPORT_ID 0xff
> +#define GEN5_APP_HID_REPORT_ID 0xf7
> +#define GEN5_BL_MAX_OUTPUT_LENGTH 0x0100
> +#define GEN5_APP_MAX_OUTPUT_LENGTH 0x00fe
> +
> +#define GEN5_BL_REPORT_DESCRIPTOR_SIZE 0x1d
> +#define GEN5_BL_REPORT_DESCRIPTOR_ID 0xfe
> +#define GEN5_APP_REPORT_DESCRIPTOR_SIZE 0xee
> +#define GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE 0xfa
> +#define GEN5_APP_REPORT_DESCRIPTOR_ID 0xf6
> +
> +#define GEN5_TOUCH_REPORT_ID 0x01
> +#define GEN5_BTN_REPORT_ID 0x03
> +#define GEN5_WAKEUP_EVENT_REPORT_ID 0x04
> +#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05
> +#define GEN5_PUSH_BTN_REPORT_ID 0x06
> +
> +#define GEN5_CMD_COMPLETE_SUCCESS(status) ((status) == 0x00)
> +
> +#define GEN5_BL_INITIATE_RESP_LEN 11
> +#define GEN5_BL_FAIL_EXIT_RESP_LEN 11
> +#define GEN5_BL_FAIL_EXIT_STATUS_CODE 0x0c
> +#define GEN5_BL_VERIFY_INTEGRITY_RESP_LEN 12
> +#define GEN5_BL_INTEGRITY_CHEKC_PASS 0x00
> +#define GEN5_BL_BLOCK_WRITE_RESP_LEN 11
> +#define GEN5_BL_READ_APP_INFO_RESP_LEN 31
> +#define GEN5_CMD_CALIBRATE 0x28
> +#define CYAPA_SENSING_MODE_MUTUAL_CAP_FINE 0x00
> +#define CYAPA_SENSING_MODE_SELF_CAP 0x02
> +
> +#define GEN5_CMD_RETRIEVE_DATA_STRUCTURE 0x24
> +#define GEN5_RETRIEVE_MUTUAL_PWC_DATA 0x00
> +#define GEN5_RETRIEVE_SELF_CAP_PWC_DATA 0x01
> +
> +#define GEN5_RETRIEVE_DATA_ELEMENT_SIZE_MASK 0x07
> +
> +#define GEN5_CMD_EXECUTE_PANEL_SCAN 0x2a
> +#define GEN5_CMD_RETRIEVE_PANEL_SCAN 0x2b
> +#define GEN5_PANEL_SCAN_MUTUAL_RAW_DATA 0x00
> +#define GEN5_PANEL_SCAN_MUTUAL_BASELINE 0x01
> +#define GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT 0x02
> +#define GEN5_PANEL_SCAN_SELF_RAW_DATA 0x03
> +#define GEN5_PANEL_SCAN_SELF_BASELINE 0x04
> +#define GEN5_PANEL_SCAN_SELF_DIFFCOUNT 0x05
> +
> +#define GEN5_PWC_DATA_ELEMENT_SIZE_MASK 0x07
> +
> +#define GEN5_NUMBER_OF_TOUCH_OFFSET 5
> +#define GEN5_NUMBER_OF_TOUCH_MASK 0x1f
> +#define GEN5_BUTTONS_OFFSET 5
> +#define GEN5_BUTTONS_MASK 0x0f
> +#define GEN5_GET_EVENT_ID(reg) (((reg) >> 5) & 0x03)
> +#define GEN5_GET_TOUCH_ID(reg) ((reg) & 0x1f)
> +
> +#define GEN5_PRODUCT_FAMILY_MASK 0xf000
> +#define GEN5_PRODUCT_FAMILY_TRACKPAD 0x1000
> +
> +#define TSG_INVALID_CMD 0xff
> +
> +struct cyapa_gen5_touch_record {
> + /*
> + * Bit 7 - 3: reserved
> + * Bit 2 - 0: touch type;
> + * 0 : standard finger;
> + * 1 - 15 : reserved.
> + */
> + u8 touch_type;
> +
> + /*
> + * Bit 7: indicates touch liftoff status.
> + * 0 : touch is currently on the panel.
> + * 1 : touch record indicates a liftoff.
> + * Bit 6 - 5: indicates an event associated with this touch instance
> + * 0 : no event
> + * 1 : touchdown
> + * 2 : significant displacement (> active distance)
> + * 3 : liftoff (record reports last known coordinates)
> + * Bit 4 - 0: An arbitrary ID tag associated with a finger
> + * to allow tracking a touch as it moves around the panel.
> + */
> + u8 touch_tip_event_id;
> +
> + /* Bit 7 - 0 of X-axis corrinate of the touch in pixel. */
coordinate

and more below...
> + u8 x_lo;
> +
> + /* Bit 15 - 8 of X-axis corrinate of the touch in pixel. */
> + u8 x_hi;
> +
> + /* Bit 7 - 0 of Y-axis corrinate of the touch in pixel. */
> + u8 y_lo;
> +
> + /* Bit 15 - 8 of Y-axis corrinate of the touch in pixel. */
> + u8 y_hi;
> +
> + /* Touch intensity in counts, pressure value. */
> + u8 z;
> +
> + /*
> + * The length of the major axis of the ellipse of contact between
> + * the finger and the panel (ABS_MT_TOUCH_MAJOR).
> + */
> + u8 major_axis_len;
> +
> + /*
> + * The length of the minor axis of the ellipse of contact between
> + * the finger and the panel (ABS_MT_TOUCH_MINOR).
> + */
> + u8 minor_axis_len;
> +
> + /*
> + * The length of the major axis of the approaching tool.
> + * (ABS_MT_WIDTH_MAJOR)
> + */
> + u8 major_tool_len;
> +
> + /*
> + * The length of the minor axis of the approaching tool.
> + * (ABS_MT_WIDTH_MINOR)
> + */
> + u8 minor_tool_len;
> +
> + /*
> + * The angle between the panel vertical axis and
> + * the major axis of the contact ellipse. This value is an 8-bit
> + * signed integer. The range is -127 to +127 (corresponding to
> + * -90 degree and +90 degree respectively).
> + * The positive direction is clockwise from the vertical axis.
> + * If the ellipse of contact degenerates into a circle,
> + * orientation is reported as 0.
> + */
> + u8 orientation;
> +} __packed;
> +
> +struct cyapa_gen5_report_data {
> + u8 report_head[GEN5_TOUCH_REPORT_HEAD_SIZE];
> + struct cyapa_gen5_touch_record touch_records[10];
> +} __packed;
> +
> +struct cyapa_tsg_bin_image_data_record {
> + u8 flash_array_id;
> + __be16 row_number;
> + /* The number of bytes of flash data contained in this record. */
> + __be16 record_len;
> + /* The flash program data. */
> + u8 record_data[CYAPA_TSG_FW_ROW_SIZE];
> +} __packed;
> +
> +struct cyapa_tsg_bin_image {
> + struct cyapa_tsg_bin_image_head image_head;
> + struct cyapa_tsg_bin_image_data_record records[0];
> +} __packed;
> +
> +/* Variables to record latest gen5 trackpad power states. */
> +#define GEN5_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s))
> +#define GEN5_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode)
> +#define GEN5_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t))
> +#define GEN5_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time)
> +#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \
> + (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
> +
> +
> +static int cyapa_gen5_initialize(struct cyapa *cyapa)
> +{
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + init_completion(&gen5_pip->cmd_ready);
> + atomic_set(&gen5_pip->cmd_issued, 0);
> + mutex_init(&gen5_pip->cmd_lock);
> +
> + gen5_pip->resp_sort_func = NULL;
> + gen5_pip->in_progress_cmd = TSG_INVALID_CMD;
> + gen5_pip->resp_data = NULL;
> + gen5_pip->resp_len = NULL;
> +
> + cyapa->dev_pwr_mode = UNINIT_PWR_MODE;
> + cyapa->dev_sleep_time = UNINIT_SLEEP_TIME;
> +
> + return 0;
> +}
> +
> +/* Return negative errno, or else the number of bytes read. */
> +static ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size)
> +{
> + int ret;
> +
> + if (size == 0)
> + return 0;
> +
> + if (!buf || size > CYAPA_REG_MAP_SIZE)
> + return -EINVAL;
> +
> + ret = i2c_master_recv(cyapa->client, buf, size);
> +
> + if (ret != size)
> + return (ret < 0) ? ret : -EIO;
> +
> + return size;
> +}
> +
> +/**
> + * Return a negative errno code else zero on success.
> + */
> +static ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size)
> +{
> + int ret;
> +
> + if (!buf || !size)
> + return -EINVAL;
> +
> + ret = i2c_master_send(cyapa->client, buf, size);
> +
> + if (ret != size)
> + return (ret < 0) ? ret : -EIO;
> +
> + return 0;
> +}
> +
> +/**
> + * This function is aimed to dump all not read data in Gen5 trackpad
> + * before send any command, otherwise, the interrupt line will be blocked.
> + */
> +static int cyapa_empty_pip_output_data(struct cyapa *cyapa,
> + u8 *buf, int *len, cb_sort func)
> +{
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> + int length;
> + int report_count;
> + int empty_count;
> + int buf_len;
> + int error;
> +
> + buf_len = 0;
> + if (len) {
> + buf_len = (*len < CYAPA_REG_MAP_SIZE) ?
> + *len : CYAPA_REG_MAP_SIZE;
> + *len = 0;
> + }
> +
> + report_count = 8; /* max 7 pending data before command response data */
> + empty_count = 0;
> + do {
> + /*
> + * Depnding on testing in cyapa driver, there are max 5 "02 00"
Depending

> + * packets between two valid buffered data report in firmware.
> + * So in order to dump all buffered data out and
> + * make interrupt line release for reassert again,
> + * we must set the empty_count check value bigger than 5 to
> + * make it work. Otherwise, in some situation,
> + * the interrupt line may unable to reactive again,
> + * which will cause trackpad device unable to
> + * report data any more.
> + * for example, it may happen in EFT and ESD testing.
> + */
> + if (empty_count > 5)
> + return 0;
> +
> + error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf,
> + GEN5_RESP_LENGTH_SIZE);
> + if (error < 0)
> + return error;
> +
> + length = get_unaligned_le16(gen5_pip->empty_buf);
> + if (length == GEN5_RESP_LENGTH_SIZE) {
> + empty_count++;
> + continue;
> + } else if (length > CYAPA_REG_MAP_SIZE) {
> + /* Should not happen */
> + return -EINVAL;
> + } else if (length == 0) {
> + /* Application or bootloader launch data polled out. */
> + length = GEN5_RESP_LENGTH_SIZE;
> + if (buf && buf_len && func &&
> + func(cyapa, gen5_pip->empty_buf, length)) {
> + length = min(buf_len, length);
> + memcpy(buf, gen5_pip->empty_buf, length);
> + *len = length;
> + /* Response found, success. */
> + return 0;
> + }
> + continue;
> + }
> +
> + error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length);
> + if (error < 0)
> + return error;
> +
> + report_count--;
> + empty_count = 0;
> + length = get_unaligned_le16(gen5_pip->empty_buf);
> + if (length <= GEN5_RESP_LENGTH_SIZE) {
> + empty_count++;
> + } else if (buf && buf_len && func &&
> + func(cyapa, gen5_pip->empty_buf, length)) {
> + length = min(buf_len, length);
> + memcpy(buf, gen5_pip->empty_buf, length);
> + *len = length;
> + /* Response found, success. */
> + return 0;
> + }
> +
> + error = -EINVAL;
> + } while (report_count);
> +
> + return error;
> +}
> +
> +static int cyapa_do_i2c_pip_cmd_irq_sync(
> + struct cyapa *cyapa,
> + u8 *cmd, size_t cmd_len,
> + unsigned long timeout)
> +{
> + int error;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + /* Wait for interrupt to set ready completion */
> + init_completion(&gen5_pip->cmd_ready);
> +
> + atomic_inc(&gen5_pip->cmd_issued);
> + error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
> + if (error) {
> + atomic_dec(&gen5_pip->cmd_issued);
> + return (error < 0) ? error : -EIO;
> + }
> +
> + /* Wait for interrupt to indicate command is completed. */
> + timeout = wait_for_completion_timeout(&gen5_pip->cmd_ready,
> + msecs_to_jiffies(timeout));
> + if (timeout == 0) {
> + atomic_dec(&gen5_pip->cmd_issued);
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +static int cyapa_do_i2c_pip_cmd_polling(
> + struct cyapa *cyapa,
> + u8 *cmd, size_t cmd_len,
> + u8 *resp_data, int *resp_len,
> + unsigned long timeout,
> + cb_sort func)
> +{
> + int error;
> + int tries;
> + int length;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + atomic_inc(&gen5_pip->cmd_issued);
> + error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
> + if (error) {
> + atomic_dec(&gen5_pip->cmd_issued);
> + return error < 0 ? error : -EIO;
> + }
> +
> + tries = timeout / 5;
> + length = resp_len ? *resp_len : 0;
> + if (resp_data && resp_len && length != 0 && func) {
> + do {
> + usleep_range(3000, 5000);
> + *resp_len = length;
> + error = cyapa_empty_pip_output_data(cyapa,
> + resp_data, resp_len, func);
> + if (error || *resp_len == 0)
> + continue;
> + else
> + break;
> + } while (--tries > 0);
> + if ((error || *resp_len == 0) || tries <= 0)
> + error = error ? error : -ETIMEDOUT;
> + }
> +
> + atomic_dec(&gen5_pip->cmd_issued);
> + return error;
> +}
> +
> +static int cyapa_i2c_pip_cmd_irq_sync(
> + struct cyapa *cyapa,
> + u8 *cmd, int cmd_len,
> + u8 *resp_data, int *resp_len,
> + unsigned long timeout,
> + cb_sort func,
> + bool irq_mode)
> +{
> + int error;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + if (!cmd || !cmd_len)
> + return -EINVAL;
> +
> + /* Commands must be serialized. */
> + error = mutex_lock_interruptible(&gen5_pip->cmd_lock);
> + if (error)
> + return error;
> +
> + gen5_pip->resp_sort_func = func;
> + gen5_pip->resp_data = resp_data;
> + gen5_pip->resp_len = resp_len;
> +
> + if (cmd_len >= GEN5_MIN_APP_CMD_LENGTH &&
> + cmd[4] == GEN5_APP_CMD_REPORT_ID) {
> + /* Application command */
> + gen5_pip->in_progress_cmd = cmd[6] & 0x7f;
> + } else if (cmd_len >= GEN5_MIN_BL_CMD_LENGTH &&
> + cmd[4] == GEN5_BL_CMD_REPORT_ID) {
> + /* Bootloader command */
> + gen5_pip->in_progress_cmd = cmd[7];
> + }
> +
> + /* Send command data, wait and read output response data's length. */
> + if (irq_mode) {
> + gen5_pip->is_irq_mode = true;
> + error = cyapa_do_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
> + timeout);
> + if (error == -ETIMEDOUT && resp_data &&
> + resp_len && *resp_len != 0 && func) {
> + /*
> + * For some old version, there was no interrupt for
> + * the command response data, so need to poll here
> + * to try to get the response data.
> + */
> + error = cyapa_empty_pip_output_data(cyapa,
> + resp_data, resp_len, func);
> + if (error || *resp_len == 0)
> + error = error ? error : -ETIMEDOUT;
> + }
> + } else {
> + gen5_pip->is_irq_mode = false;
> + error = cyapa_do_i2c_pip_cmd_polling(cyapa, cmd, cmd_len,
> + resp_data, resp_len, timeout, func);
> + }
> +
> + gen5_pip->resp_sort_func = NULL;
> + gen5_pip->resp_data = NULL;
> + gen5_pip->resp_len = NULL;
> + gen5_pip->in_progress_cmd = TSG_INVALID_CMD;
> +
> + mutex_unlock(&gen5_pip->cmd_lock);
> + return error;
> +}
> +
> +static bool cyapa_gen5_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa,
> + u8 *data, int len)
> +{
> + if (!data || len < GEN5_MIN_BL_RESP_LENGTH)
> + return false;
> +
> + /* Bootloader input report id 30h */
> + if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_RESP_REPORT_ID &&
> + data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY &&
> + data[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY)
> + return true;
> +
> + return false;
> +}
> +
> +static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa,
> + u8 *data, int len)
> +{
> + int resp_len;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + if (!data || len < GEN5_MIN_APP_RESP_LENGTH)
> + return false;
> +
> + if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID &&
> + data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) {
> + resp_len = get_unaligned_le16(&data[GEN5_RESP_LENGTH_OFFSET]);
> + if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == 0x00 &&
> + resp_len == GEN5_UNSUPPORTED_CMD_RESP_LENGTH &&
> + data[5] == gen5_pip->in_progress_cmd) {
> + /* Unsupported command code */
> + return false;
> + } else if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) ==
> + gen5_pip->in_progress_cmd) {
> + /* Correct command response received */
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static bool cyapa_gen5_sort_application_launch_data(struct cyapa *cyapa,
> + u8 *buf, int len)
> +{
> + if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
> + return false;
> +
> + /*
> + * After reset or power on, trackpad device always sets to 0x00 0x00
> + * to indicate a reset or power on event.
> + */
> + if (buf[0] == 0 && buf[1] == 0)
> + return true;
> +
> + return false;
> +}
> +
> +static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa,
> + u8 *buf, int len)
> +{
> + int resp_len;
> + int max_output_len;
> +
> + /* Check hid descriptor. */
> + if (len != GEN5_HID_DESCRIPTOR_SIZE)
> + return false;
> +
> + resp_len = get_unaligned_le16(&buf[GEN5_RESP_LENGTH_OFFSET]);
> + max_output_len = get_unaligned_le16(&buf[16]);
> + if (resp_len == GEN5_HID_DESCRIPTOR_SIZE) {
> + if (buf[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_HID_REPORT_ID &&
> + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
> + /* BL mode HID Descriptor */
> + return true;
> + } else if (buf[2] == GEN5_APP_HID_REPORT_ID &&
> + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
> + /* APP mode HID Descriptor */
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static bool cyapa_gen5_sort_deep_sleep_data(struct cyapa *cyapa,
> + u8 *buf, int len)
> +{
> + if (len == GEN5_DEEP_SLEEP_RESP_LENGTH &&
> + buf[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_APP_DEEP_SLEEP_REPORT_ID &&
> + (buf[4] & GEN5_DEEP_SLEEP_OPCODE_MASK) ==
> + GEN5_DEEP_SLEEP_OPCODE)
> + return true;
> + return false;
> +}
> +
> +static int gen5_idle_state_parse(struct cyapa *cyapa)
> +{
> + int ret;
> + int error;
> + int length;
> + u8 cmd[2];
> + u8 resp_data[GEN5_HID_DESCRIPTOR_SIZE];
> + int max_output_len;
> +
> + /*
> + * Dump all buffered data firstly for the situation
> + * when the trackpad is just power on the cyapa go here.
> + */
> + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
> +
> + memset(resp_data, 0, sizeof(resp_data));
> + ret = cyapa_i2c_pip_read(cyapa, resp_data, 3);
> + if (ret != 3)
> + return ret < 0 ? ret : -EIO;
> +
> + length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]);
> + if (length == GEN5_RESP_LENGTH_SIZE) {
> + /* Normal state of Gen5 with no data to respose */
> + cyapa->gen = CYAPA_GEN5;
> +
> + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
> +
> + /* Read description from trackpad device */
> + cmd[0] = 0x01;
> + cmd[1] = 0x00;
> + length = GEN5_HID_DESCRIPTOR_SIZE;
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
> + cmd, GEN5_RESP_LENGTH_SIZE,
> + resp_data, &length,
> + 300,
> + cyapa_gen5_sort_hid_descriptor_data,
> + false);
> + if (error)
> + return error;
> +
> + length = get_unaligned_le16(
> + &resp_data[GEN5_RESP_LENGTH_OFFSET]);
> + max_output_len = get_unaligned_le16(&resp_data[16]);
> + if ((length == GEN5_HID_DESCRIPTOR_SIZE ||
> + length == GEN5_RESP_LENGTH_SIZE) &&
> + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_HID_REPORT_ID) &&
> + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
> + /* BL mode HID Description read */
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else if ((length == GEN5_HID_DESCRIPTOR_SIZE ||
> + length == GEN5_RESP_LENGTH_SIZE) &&
> + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_APP_HID_REPORT_ID) &&
> + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
> + /* APP mode HID Description read */
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + } else {
> + /* Should not happen!!! */
> + cyapa->state = CYAPA_STATE_NO_DEVICE;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data)
> +{
> + int ret;
> + int length;
> + u8 resp_data[32];
> + int max_output_len;
> +
> + /* 0x20 0x00 0xF7 is Gen5 Application HID Description Header;
> + * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header.
> + *
> + * Must read HID Description content through out,
> + * otherwise Gen5 trackpad cannot response next command
> + * or report any touch or button data.
> + */
> + ret = cyapa_i2c_pip_read(cyapa, resp_data,
> + GEN5_HID_DESCRIPTOR_SIZE);
> + if (ret != GEN5_HID_DESCRIPTOR_SIZE)
> + return ret < 0 ? ret : -EIO;
> + length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]);
> + max_output_len = get_unaligned_le16(&resp_data[16]);
> + if (length == GEN5_RESP_LENGTH_SIZE) {
> + if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_HID_REPORT_ID) {
> + /*
> + * BL mode HID Description has been previously
> + * read out.
> + */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else {
> + /*
> + * APP mode HID Description has been previously
> + * read out.
> + */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + }
> + } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
> + resp_data[2] == GEN5_BL_HID_REPORT_ID &&
> + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
> + /* BL mode HID Description read. */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
> + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_APP_HID_REPORT_ID) &&
> + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
> + /* APP mode HID Description read. */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + } else {
> + /* Should not happen!!! */
> + cyapa->state = CYAPA_STATE_NO_DEVICE;
> + }
> +
> + return 0;
> +}
> +
> +static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data)
> +{
> + int length;
> +
> + length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
> + switch (reg_data[GEN5_RESP_REPORT_ID_OFFSET]) {
> + case GEN5_TOUCH_REPORT_ID:
> + if (length < GEN5_TOUCH_REPORT_HEAD_SIZE ||
> + length > GEN5_TOUCH_REPORT_MAX_SIZE)
> + return -EINVAL;
> + break;
> + case GEN5_BTN_REPORT_ID:
> + case GEN5_OLD_PUSH_BTN_REPORT_ID:
> + case GEN5_PUSH_BTN_REPORT_ID:
> + if (length < GEN5_BTN_REPORT_HEAD_SIZE ||
> + length > GEN5_BTN_REPORT_MAX_SIZE)
> + return -EINVAL;
> + break;
> + case GEN5_WAKEUP_EVENT_REPORT_ID:
> + if (length != GEN5_WAKEUP_EVENT_SIZE)
> + return -EINVAL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + return 0;
> +}
> +
> +static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data)
> +{
> + int ret;
> + int length;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + /*
> + * Must read report data through out,
> + * otherwise Gen5 trackpad cannot response next command
> + * or report any touch or button data.
> + */
> + length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
> + ret = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length);
> + if (ret != length)
> + return ret < 0 ? ret : -EIO;
> +
> + if (length == GEN5_RESP_LENGTH_SIZE) {
> + /* Previous command has read the data through out. */
> + if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_RESP_REPORT_ID) {
> + /* Gen5 BL command response data detected */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else {
> + /* Gen5 APP command response data detected */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + }
> + } else if ((gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_RESP_REPORT_ID) &&
> + (gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] ==
> + GEN5_RESP_RSVD_KEY) &&
> + (gen5_pip->empty_buf[GEN5_RESP_BL_SOP_OFFSET] ==
> + GEN5_SOP_KEY) &&
> + (gen5_pip->empty_buf[length - 1] ==
> + GEN5_EOP_KEY)) {
> + /* Gen5 BL command response data detected */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else if (gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_APP_RESP_REPORT_ID &&
> + gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] ==
> + GEN5_RESP_RSVD_KEY) {
> + /* Gen5 APP command response data detected */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + } else {
> + /* Should not happen!!! */
> + cyapa->state = CYAPA_STATE_NO_DEVICE;
> + }
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
> +{
> + int length;
> +
> + if (!reg_data || len < 3)
> + return -EINVAL;
> +
> + cyapa->state = CYAPA_STATE_NO_DEVICE;
> +
> + /* Parse based on Gen5 characteristic registers and bits */
> + length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
> + if (length == 0 || length == GEN5_RESP_LENGTH_SIZE) {
> + gen5_idle_state_parse(cyapa);
> + } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
> + (reg_data[2] == GEN5_BL_HID_REPORT_ID ||
> + reg_data[2] == GEN5_APP_HID_REPORT_ID)) {
> + gen5_hid_description_header_parse(cyapa, reg_data);
> + } else if ((length == GEN5_APP_REPORT_DESCRIPTOR_SIZE ||
> + length == GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE) &&
> + reg_data[2] == GEN5_APP_REPORT_DESCRIPTOR_ID) {
> + /* 0xEE 0x00 0xF6 is Gen5 APP Report Description header. */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> + } else if (length == GEN5_BL_REPORT_DESCRIPTOR_SIZE &&
> + reg_data[2] == GEN5_BL_REPORT_DESCRIPTOR_ID) {
> + /* 0x1D 0x00 0xFE is Gen5 BL Report Descriptior header. */
> + cyapa->gen = CYAPA_GEN5;
> + cyapa->state = CYAPA_STATE_GEN5_BL;
> + } else if (reg_data[2] == GEN5_TOUCH_REPORT_ID ||
> + reg_data[2] == GEN5_BTN_REPORT_ID ||
> + reg_data[2] == GEN5_OLD_PUSH_BTN_REPORT_ID ||
> + reg_data[2] == GEN5_PUSH_BTN_REPORT_ID ||
> + reg_data[2] == GEN5_WAKEUP_EVENT_REPORT_ID) {
> + gen5_report_data_header_parse(cyapa, reg_data);
> + } else if (reg_data[2] == GEN5_BL_RESP_REPORT_ID ||
> + reg_data[2] == GEN5_APP_RESP_REPORT_ID) {
> + gen5_cmd_resp_header_parse(cyapa, reg_data);
> + }
> +
> + if (cyapa->gen == CYAPA_GEN5) {
> + /*
> + * Must read the content (e.g.: Report Description and so on)
> + * from trackpad device through out. Otherwise,
> + * Gen5 trackpad cannot response to next command or
> + * report any touch or button data later.
> + */
> + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
> +
> + if (cyapa->state == CYAPA_STATE_GEN5_APP ||
> + cyapa->state == CYAPA_STATE_GEN5_BL)
> + return 0;
> + }
> +
> + return -EAGAIN;
> +}
> +
> +bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
> +{
> + if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
> + return false;
> +
> + if (buf[0] == 0 && buf[1] == 0)
> + return true;
> +
> + /* Exit bootloader failed for some reason. */
> + if (len == GEN5_BL_FAIL_EXIT_RESP_LEN &&
> + buf[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_RESP_REPORT_ID &&
> + buf[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY &&
> + buf[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY &&
> + buf[10] == GEN5_EOP_KEY)
> + return true;
> +
> + return false;
> +}
> +
> +static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
> +{
> + int error;
> + u8 resp_data[11];
> + int resp_len;
> + u8 bl_gen5_bl_exit[] = { 0x04, 0x00,
> + 0x0B, 0x00, 0x40, 0x00, 0x01, 0x3b, 0x00, 0x00,
> + 0x20, 0xc7, 0x17
> + };
> +
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
> + bl_gen5_bl_exit, sizeof(bl_gen5_bl_exit),
> + resp_data, &resp_len,
> + 5000, cyapa_gen5_sort_bl_exit_data, false);
> + if (error)
> + return error;
> +
> + if (resp_len == GEN5_BL_FAIL_EXIT_RESP_LEN ||
> + resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
> + GEN5_BL_RESP_REPORT_ID)
> + return -EAGAIN;
> +
> + if (resp_data[0] == 0x00 && resp_data[1] == 0x00)
> + return 0;
> +
> + return -ENODEV;
> +}
> +
> +static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
> +{
> + u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 };
> + u8 resp_data[6];
> + int resp_len;
> + int error;
> +
> + cmd[7] = power_state;
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
> + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x08) ||
> + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
> + return error < 0 ? error : -EINVAL;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_set_interval_time(struct cyapa *cyapa,
> + u8 parameter_id, u16 interval_time)
> +{
> + u8 cmd[13];
> + int cmd_len;
> + u8 resp_data[7];
> + int resp_len;
> + u8 parameter_size;
> + int error;
> +
> + switch (parameter_id) {
> + case GEN5_PARAMETER_ACT_INTERVL_ID:
> + parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
> + break;
> + case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
> + parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
> + break;
> + case GEN5_PARAMETER_LP_INTRVL_ID:
> + parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
> + break;
> + default:
> + return -EINVAL;
> + }
> + cmd_len = 7 + parameter_size; /* Not incuding 2 bytes address */
> + cmd[0] = 0x04;
> + cmd[1] = 0x00;
> + put_unaligned_le16(cmd_len, &cmd[2]);
> + cmd[4] = 0x2f;
> + cmd[5] = 0x00;
> + cmd[6] = 0x06; /* Set parameter command code */
> + cmd[7] = parameter_id;
> + cmd[8] = parameter_size;
> + put_unaligned_le16(interval_time, &cmd[9]);
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len + 2,
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
> + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x06) ||
> + resp_data[5] != parameter_id ||
> + resp_data[6] != parameter_size)
> + return error < 0 ? error : -EINVAL;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_get_interval_time(struct cyapa *cyapa,
> + u8 parameter_id, u16 *interval_time)
> +{
> + u8 cmd[8];
> + u8 resp_data[11];
> + int resp_len;
> + u8 parameter_size;
> + u16 mask, i;
> + int error;
> +
> + *interval_time = 0;
> + switch (parameter_id) {
> + case GEN5_PARAMETER_ACT_INTERVL_ID:
> + parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
> + break;
> + case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
> + parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
> + break;
> + case GEN5_PARAMETER_LP_INTRVL_ID:
> + parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + cmd[0] = 0x04;
> + cmd[1] = 0x00;
> + cmd[2] = 0x06;
> + cmd[3] = 0x00;
> + cmd[4] = 0x2f;
> + cmd[5] = 0x00;
> + cmd[6] = 0x05; /* Get parameter command code */
> + cmd[7] = parameter_id;
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
> + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x05) ||
> + resp_data[5] != parameter_id ||
> + resp_data[6] == 0)
> + return error < 0 ? error : -EINVAL;
> +
> + mask = 0;
> + for (i = 0; i < parameter_size; i++)
> + mask |= (0xff << (i * 8));
> + *interval_time = get_unaligned_le16(&resp_data[7]) & mask;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa)
> +{
> + u8 cmd[10];
> + u8 resp_data[7];
> + int resp_len;
> + int error;
> +
> + cmd[0] = 0x04;
> + cmd[1] = 0x00;
> + put_unaligned_le16(8, &cmd[2]);
> + cmd[4] = 0x2f;
> + cmd[5] = 0x00;
> + cmd[6] = 0x06; /* Set parameter command code */
> + cmd[7] = GEN5_PARAMETER_DISABLE_PIP_REPORT;
> + cmd[8] = 0x01;
> + cmd[9] = 0x01;
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, 10,
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
> + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x06) ||
> + resp_data[5] != GEN5_PARAMETER_DISABLE_PIP_REPORT ||
> + resp_data[6] != 0x01)
> + return error < 0 ? error : -EINVAL;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_deep_sleep(struct cyapa *cyapa, u8 state)
> +{
> + u8 cmd[4] = { 0x05, 0x00, 0x00, 0x08};
> + u8 resp_data[5];
> + int resp_len;
> + int error;
> +
> + cmd[0] = 0x05;
> + cmd[1] = 0x00;
> + cmd[2] = state & GEN5_DEEP_SLEEP_STATE_MASK;
> + cmd[3] = 0x08;
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_deep_sleep_data, false);
> + if (error || ((resp_data[3] & GEN5_DEEP_SLEEP_STATE_MASK) != state))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
> + u8 power_mode, u16 sleep_time)
> +{
> + struct device *dev = &cyapa->client->dev;
> + u8 power_state;
> + int error;
> +
> + if (cyapa->state != CYAPA_STATE_GEN5_APP)
> + return 0;
> +
> + /* Dump all the report data before do power mode commmands. */
> + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
> +
> + if (GEN5_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
> + /*
> + * Assume TP in deep sleep mode when driver is loaded,
> + * avoid driver unload and reload command IO issue caused by TP
> + * has been set into deep sleep mode when unloading.
> + */
> + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
> + }
> +
> + if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) &&
> + GEN5_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
> + if (cyapa_gen5_get_interval_time(cyapa,
> + GEN5_PARAMETER_LP_INTRVL_ID,
> + &cyapa->dev_sleep_time) != 0)
> + GEN5_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
> +
> + if (GEN5_DEV_GET_PWR_STATE(cyapa) == power_mode) {
> + if (power_mode == PWR_MODE_OFF ||
> + power_mode == PWR_MODE_FULL_ACTIVE ||
> + power_mode == PWR_MODE_BTN_ONLY ||
> + GEN5_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
> + /* Has in correct power mode state, early return. */
> + return 0;
> + }
> + }
> +
> + if (power_mode == PWR_MODE_OFF) {
> + error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_OFF);
> + if (error) {
> + dev_err(dev, "enter deep sleep fail: %d\n", error);
> + return error;
> + }
> +
> + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
> + return 0;
> + }
> +
> + /*
> + * When trackpad in power off mode, it cannot change to other power
> + * state directly, must be wake up from sleep firstly, then
> + * continue to do next power sate change.
> + */
> + if (GEN5_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
> + error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_ON);
> + if (error) {
> + dev_err(dev, "deep sleep wake fail: %d\n", error);
> + return error;
> + }
> + }
> +
> + if (power_mode == PWR_MODE_FULL_ACTIVE) {
> + error = cyapa_gen5_change_power_state(cyapa,
> + GEN5_POWER_STATE_ACTIVE);
> + if (error) {
> + dev_err(dev, "change to active fail: %d\n", error);
> + return error;
> + }
> +
> + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
> + } else if (power_mode == PWR_MODE_BTN_ONLY) {
> + error = cyapa_gen5_change_power_state(cyapa,
> + GEN5_POWER_STATE_BTN_ONLY);
> + if (error) {
> + dev_err(dev, "fail to button only mode: %d\n", error);
> + return error;
> + }
> +
> + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
> + } else {
> + /*
> + * Continue to change power mode even failed to set
> + * interval time, it won't affect the power mode change.
> + * except the sleep interval time is not correct.
> + */
> + if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) ||
> + sleep_time != GEN5_DEV_GET_SLEEP_TIME(cyapa))
> + if (cyapa_gen5_set_interval_time(cyapa,
> + GEN5_PARAMETER_LP_INTRVL_ID,
> + sleep_time) == 0)
> + GEN5_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
> +
> + if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME)
> + power_state = GEN5_POWER_STATE_READY;
> + else
> + power_state = GEN5_POWER_STATE_IDLE;
> + error = cyapa_gen5_change_power_state(cyapa, power_state);
> + if (error) {
> + dev_err(dev, "set power state to 0x%02x failed: %d\n",
> + power_state, error);
> + return error;
> + }
> +
> + /*
> + * Disable pip report for a little time, firmware will
> + * re-enable it automatically. It's used to fix the issue
> + * that trackpad unable to report signal to wake system up
> + * in the special situation that system is in suspending, and
> + * at the same time, user touch trackpad to wake system up.
> + * This function can avoid the data to be buffured when system
> + * is suspending which may cause interrput line unable to be
> + * asserted again.
> + */
> + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
> + cyapa_gen5_disable_pip_report(cyapa);
> +
> + GEN5_DEV_SET_PWR_STATE(cyapa,
> + cyapa_sleep_time_to_pwr_cmd(sleep_time));
> + }
> +
> + return 0;
> +}
> +
> +static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
> + u8 *buf, int len)
> +{
> + /* Check the report id and command code */
> + if (VALID_CMD_RESP_HEADER(buf, 0x02))
> + return true;
> +
> + return false;
> +}
> +
> +static int cyapa_gen5_bl_query_data(struct cyapa *cyapa)
> +{
> + u8 cmd[16];
> + int cmd_len;
> + u8 resp_data[GEN5_BL_READ_APP_INFO_RESP_LEN];
> + int resp_len;
> + int error;
> +
> + /* Read application information. */
> + cmd[0] = 0x04;
> + cmd[1] = 0x00;
> + cmd[2] = 0x0b;
> + cmd[3] = 0x00;
> + cmd[4] = 0x40;
> + cmd[5] = 0x00;
> + cmd[6] = GEN5_SOP_KEY;
> + cmd[7] = 0x3c; /* Read application information command code */
> + cmd[8] = 0x00;
> + cmd[9] = 0x00;
> + cmd[10] = 0xb0;
> + cmd[11] = 0x42;
> + cmd[12] = GEN5_EOP_KEY;
> + cmd_len = 13;
> + resp_len = GEN5_BL_READ_APP_INFO_RESP_LEN;
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
> + cmd, cmd_len,
> + resp_data, &resp_len,
> + 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, false);
> + if (error || resp_len != GEN5_BL_READ_APP_INFO_RESP_LEN ||
> + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
> + return error ? error : -EIO;
> +
> + memcpy(&cyapa->product_id[0], &resp_data[8], 5);
> + cyapa->product_id[5] = '-';
> + memcpy(&cyapa->product_id[6], &resp_data[13], 6);
> + cyapa->product_id[12] = '-';
> + memcpy(&cyapa->product_id[13], &resp_data[19], 2);
> + cyapa->product_id[15] = '\0';
> +
> + cyapa->fw_maj_ver = resp_data[22];
> + cyapa->fw_min_ver = resp_data[23];
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_get_query_data(struct cyapa *cyapa)
> +{
> + u8 resp_data[71];
> + int resp_len;
> + u8 get_system_information[] = {
> + 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02
> + };
> + u16 product_family;
> + int error;
> +
> + resp_len = sizeof(resp_data);
> + error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
> + get_system_information, sizeof(get_system_information),
> + resp_data, &resp_len,
> + 2000, cyapa_gen5_sort_system_info_data, false);
> + if (error || resp_len < sizeof(resp_data))
> + return error ? error : -EIO;
> +
> + product_family = get_unaligned_le16(&resp_data[7]);
> + if ((product_family & GEN5_PRODUCT_FAMILY_MASK) !=
> + GEN5_PRODUCT_FAMILY_TRACKPAD)
> + return -EINVAL;
> +
> + cyapa->fw_maj_ver = resp_data[15];
> + cyapa->fw_min_ver = resp_data[16];
> +
> + cyapa->electrodes_x = resp_data[52];
> + cyapa->electrodes_y = resp_data[53];
> +
> + cyapa->physical_size_x = get_unaligned_le16(&resp_data[54]) / 100;
> + cyapa->physical_size_y = get_unaligned_le16(&resp_data[56]) / 100;
> +
> + cyapa->max_abs_x = get_unaligned_le16(&resp_data[58]);
> + cyapa->max_abs_y = get_unaligned_le16(&resp_data[60]);
> +
> + cyapa->max_z = get_unaligned_le16(&resp_data[62]);
> +
> + cyapa->x_origin = resp_data[64] & 0x01;
> + cyapa->y_origin = resp_data[65] & 0x01;
> +
> + cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
> +
> + memcpy(&cyapa->product_id[0], &resp_data[33], 5);
> + cyapa->product_id[5] = '-';
> + memcpy(&cyapa->product_id[6], &resp_data[38], 6);
> + cyapa->product_id[12] = '-';
> + memcpy(&cyapa->product_id[13], &resp_data[44], 2);
> + cyapa->product_id[15] = '\0';
> +
> + if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
> + !cyapa->physical_size_x || !cyapa->physical_size_y ||
> + !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
> +{
> + struct device *dev = &cyapa->client->dev;
> + int error;
> +
> + if (cyapa->gen != CYAPA_GEN5)
> + return -ENODEV;
> +
> + switch (cyapa->state) {
> + case CYAPA_STATE_GEN5_BL:
> + error = cyapa_gen5_bl_exit(cyapa);
> + if (error) {
> + /* Rry to update trackpad product information. */
> + cyapa_gen5_bl_query_data(cyapa);
> + goto out;
> + }
> +
> + cyapa->state = CYAPA_STATE_GEN5_APP;
> +
> + case CYAPA_STATE_GEN5_APP:
> + /*
> + * If trackpad device in deep sleep mode,
> + * the app command will fail.
> + * So always try to reset trackpad device to full active when
> + * the device state is requeried.
> + */
> + error = cyapa_gen5_set_power_mode(cyapa,
> + PWR_MODE_FULL_ACTIVE, 0);
> + if (error)
> + dev_warn(dev, "%s: failed to set power active mode.\n",
> + __func__);
> +
> + /* Get trackpad product information. */
> + error = cyapa_gen5_get_query_data(cyapa);
> + if (error)
> + goto out;
> + /* Only support product ID starting with CYTRA */
> + if (memcmp(cyapa->product_id, product_id,
> + strlen(product_id)) != 0) {
> + dev_err(dev, "%s: unknown product ID (%s)\n",
> + __func__, cyapa->product_id);
> + error = -EINVAL;
> + }
> + break;
> + default:
> + error = -EINVAL;
> + }
> +
> +out:
> + return error;
> +}
> +
> +/*
> + * Return false, do not continue process
> + * Return true, continue process.
> + */
> +static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa)
> +{
> + int length;
> + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
> +
> + if (atomic_read(&gen5_pip->cmd_issued)) {
> + /* Polling command response data. */
> + if (gen5_pip->is_irq_mode == false)
> + return false;
> +
> + /*
> + * Read out all none command response data.
> + * these output data may caused by user put finger on
> + * trackpad when host waiting the command response.
> + */
> + cyapa_i2c_pip_read(cyapa, gen5_pip->irq_cmd_buf,
> + GEN5_RESP_LENGTH_SIZE);
> + length = get_unaligned_le16(gen5_pip->irq_cmd_buf);
> + length = (length <= GEN5_RESP_LENGTH_SIZE) ?
> + GEN5_RESP_LENGTH_SIZE : length;
> + if (length > GEN5_RESP_LENGTH_SIZE)
> + cyapa_i2c_pip_read(cyapa,
> + gen5_pip->irq_cmd_buf, length);
> +
> + if (!(gen5_pip->resp_sort_func &&
> + gen5_pip->resp_sort_func(cyapa,
> + gen5_pip->irq_cmd_buf, length))) {
> + /*
> + * Cover the Gen5 V1 firmware issue.
> + * The issue is there is no interrut will be
> + * asserted to notityf host to read a command
> + * data out when always has finger touch on
> + * trackpad during the command is issued to
> + * trackad device.
> + * This issue has the scenario is that,
> + * user always has his fingers touched on
> + * trackpad device when booting/rebooting
> + * their chrome book.
> + */
> + length = *gen5_pip->resp_len;
> + cyapa_empty_pip_output_data(cyapa,
> + gen5_pip->resp_data,
> + &length,
> + gen5_pip->resp_sort_func);
> + if (gen5_pip->resp_len && length != 0) {
> + *gen5_pip->resp_len = length;
> + atomic_dec(&gen5_pip->cmd_issued);
> + complete(&gen5_pip->cmd_ready);
> + }
> + return false;
> + }
> +
> + if (gen5_pip->resp_data && gen5_pip->resp_len) {
> + *gen5_pip->resp_len = (*gen5_pip->resp_len < length) ?
> + *gen5_pip->resp_len : length;
> + memcpy(gen5_pip->resp_data, gen5_pip->irq_cmd_buf,
> + *gen5_pip->resp_len);
> + }
> + atomic_dec(&gen5_pip->cmd_issued);
> + complete(&gen5_pip->cmd_ready);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void cyapa_gen5_report_buttons(struct cyapa *cyapa,
> + const struct cyapa_gen5_report_data *report_data)
> +{
> + struct input_dev *input = cyapa->input;
> + u8 buttons = report_data->report_head[GEN5_BUTTONS_OFFSET];
> +
> + buttons = (buttons << CAPABILITY_BTN_SHIFT) & CAPABILITY_BTN_MASK;
> +
> + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) {
> + input_report_key(input, BTN_LEFT,
> + !!(buttons & CAPABILITY_LEFT_BTN_MASK));
> + }
> + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) {
> + input_report_key(input, BTN_MIDDLE,
> + !!(buttons & CAPABILITY_MIDDLE_BTN_MASK));
> + }
> + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) {
> + input_report_key(input, BTN_RIGHT,
> + !!(buttons & CAPABILITY_RIGHT_BTN_MASK));
> + }
> +
> + input_sync(input);
> +}
> +
> +static void cyapa_gen5_report_slot_data(struct cyapa *cyapa,
> + const struct cyapa_gen5_touch_record *touch)
> +{
> + struct input_dev *input = cyapa->input;
> + u8 event_id = GEN5_GET_EVENT_ID(touch->touch_tip_event_id);
> + int slot = GEN5_GET_TOUCH_ID(touch->touch_tip_event_id);
> + int x, y;
> +
> + if (event_id == RECORD_EVENT_LIFTOFF)
> + return;
> +
> + input_mt_slot(input, slot);
> + input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
> + x = (touch->x_hi << 8) | touch->x_lo;
> + if (cyapa->x_origin)
> + x = cyapa->max_abs_x - x;
> + input_report_abs(input, ABS_MT_POSITION_X, x);
> + y = (touch->y_hi << 8) | touch->y_lo;
> + if (cyapa->y_origin)
> + y = cyapa->max_abs_y - y;
> + input_report_abs(input, ABS_MT_POSITION_Y, y);
> + input_report_abs(input, ABS_MT_PRESSURE,
> + touch->z);
> + input_report_abs(input, ABS_MT_TOUCH_MAJOR,
> + touch->major_axis_len);
> + input_report_abs(input, ABS_MT_TOUCH_MINOR,
> + touch->minor_axis_len);
> +
> + input_report_abs(input, ABS_MT_WIDTH_MAJOR,
> + touch->major_tool_len);
> + input_report_abs(input, ABS_MT_WIDTH_MINOR,
> + touch->minor_tool_len);
> +
> + input_report_abs(input, ABS_MT_ORIENTATION,
> + touch->orientation);
> +}
> +
> +static void cyapa_gen5_report_touches(struct cyapa *cyapa,
> + const struct cyapa_gen5_report_data *report_data)
> +{
> + struct input_dev *input = cyapa->input;
> + unsigned int touch_num;
> + int i;
> +
> + touch_num = report_data->report_head[GEN5_NUMBER_OF_TOUCH_OFFSET] &
> + GEN5_NUMBER_OF_TOUCH_MASK;
> +
> + for (i = 0; i < touch_num; i++)
> + cyapa_gen5_report_slot_data(cyapa,
> + &report_data->touch_records[i]);
> +
> + input_mt_sync_frame(input);
> + input_sync(input);
> +}
> +
> +static int cyapa_gen5_irq_handler(struct cyapa *cyapa)
> +{
> + struct device *dev = &cyapa->client->dev;
> + struct cyapa_gen5_report_data report_data;
> + int ret;
> + u8 report_id;
> + unsigned int report_len;
> +
> + if (cyapa->gen != CYAPA_GEN5 ||
> + cyapa->state != CYAPA_STATE_GEN5_APP) {
> + dev_err(dev, "invalid device state, gen=%d, state=0x%02x\n",
> + cyapa->gen, cyapa->state);
> + return -EINVAL;
> + }
> +
> + ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data,
> + GEN5_RESP_LENGTH_SIZE);
> + if (ret != GEN5_RESP_LENGTH_SIZE) {
> + dev_err(dev, "failed to read length bytes, (%d)\n", ret);
> + return -EINVAL;
> + }
> +
> + report_len = get_unaligned_le16(
> + &report_data.report_head[GEN5_RESP_LENGTH_OFFSET]);
> + if (report_len < GEN5_RESP_LENGTH_SIZE) {
> + /* Invliad length or internal reset happened. */
> + dev_err(dev, "invalid report_len=%d. bytes: %02x %02x\n",
> + report_len, report_data.report_head[0],
> + report_data.report_head[1]);
> + return -EINVAL;
> + }
> +
> + /* Idle, no data for report. */
> + if (report_len == GEN5_RESP_LENGTH_SIZE)
> + return 0;
> +
> + ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, report_len);
> + if (ret != report_len) {
> + dev_err(dev, "failed to read %d bytes report data, (%d)\n",
> + report_len, ret);
> + return -EINVAL;
> + }
> +
> + report_id = report_data.report_head[GEN5_RESP_REPORT_ID_OFFSET];
> + if (report_id == GEN5_WAKEUP_EVENT_REPORT_ID &&
> + report_len == GEN5_WAKEUP_EVENT_SIZE) {
> + /*
> + * Device wake event from deep sleep mode for touch.
> + * This interrupt event is used to wake system up.
> + */
> + return 0;
> + } else if (report_id != GEN5_TOUCH_REPORT_ID &&
> + report_id != GEN5_BTN_REPORT_ID &&
> + report_id != GEN5_OLD_PUSH_BTN_REPORT_ID &&
> + report_id != GEN5_PUSH_BTN_REPORT_ID) {
> + /* Running in BL mode or unknown response data read. */
> + dev_err(dev, "invalid report_id=0x%02x\n", report_id);
> + return -EINVAL;
> + }
> +
> + if (report_id == GEN5_TOUCH_REPORT_ID &&
> + (report_len < GEN5_TOUCH_REPORT_HEAD_SIZE ||
> + report_len > GEN5_TOUCH_REPORT_MAX_SIZE)) {
> + /* Invalid report data length for finger packet. */
> + dev_err(dev, "invalid touch packet length=%d\n", report_len);
> + return 0;
> + }
> +
> + if ((report_id == GEN5_BTN_REPORT_ID ||
> + report_id == GEN5_OLD_PUSH_BTN_REPORT_ID ||
> + report_id == GEN5_PUSH_BTN_REPORT_ID) &&
> + (report_len < GEN5_BTN_REPORT_HEAD_SIZE ||
> + report_len > GEN5_BTN_REPORT_MAX_SIZE)) {
> + /* Invalid report data length of button packet. */
> + dev_err(dev, "invalid button packet length=%d\n", report_len);
> + return 0;
> + }
> +
> + if (report_id == GEN5_TOUCH_REPORT_ID)
> + cyapa_gen5_report_touches(cyapa, &report_data);
> + else
> + cyapa_gen5_report_buttons(cyapa, &report_data);
> +
> + return 0;
> +}
> +
> +const struct cyapa_dev_ops cyapa_gen5_ops = {
> + .initialize = cyapa_gen5_initialize,
> +
> + .state_parse = cyapa_gen5_state_parse,
> + .operational_check = cyapa_gen5_do_operational_check,
> +
> + .irq_handler = cyapa_gen5_irq_handler,
> + .irq_cmd_handler = cyapa_gen5_irq_cmd_handler,
> + .sort_empty_output_data = cyapa_empty_pip_output_data,
> + .set_power_mode = cyapa_gen5_set_power_mode,
> +};
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
- Jeremiah Mahler
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/