From b709f4d97bb33d74ff5a14f8ff8c617b5f605c67 Mon Sep 17 00:00:00 2001 From: davidycchen Date: Tue, 17 Aug 2021 14:00:30 +0800 Subject: synaptics: add initial driver Initial driver provided by Synaptics. Bug: 198228556 Signed-off-by: davidycchen Change-Id: I471eda97a2b2e06a4dd9328e69c71d5f8e8fdc93 --- tcm/synaptics_touchcom_core_dev.h | 1096 ++++++++++++++++ tcm/synaptics_touchcom_core_v1.c | 1078 ++++++++++++++++ tcm/synaptics_touchcom_core_v2.c | 1445 +++++++++++++++++++++ tcm/synaptics_touchcom_func_base.c | 1678 ++++++++++++++++++++++++ tcm/synaptics_touchcom_func_base.h | 418 ++++++ tcm/synaptics_touchcom_func_base_flash.h | 569 ++++++++ tcm/synaptics_touchcom_func_reflash.c | 2074 ++++++++++++++++++++++++++++++ tcm/synaptics_touchcom_func_reflash.h | 145 +++ tcm/synaptics_touchcom_func_romboot.c | 1586 +++++++++++++++++++++++ tcm/synaptics_touchcom_func_romboot.h | 155 +++ tcm/synaptics_touchcom_func_touch.c | 877 +++++++++++++ tcm/synaptics_touchcom_func_touch.h | 235 ++++ 12 files changed, 11356 insertions(+) create mode 100644 tcm/synaptics_touchcom_core_dev.h create mode 100644 tcm/synaptics_touchcom_core_v1.c create mode 100644 tcm/synaptics_touchcom_core_v2.c create mode 100644 tcm/synaptics_touchcom_func_base.c create mode 100644 tcm/synaptics_touchcom_func_base.h create mode 100644 tcm/synaptics_touchcom_func_base_flash.h create mode 100644 tcm/synaptics_touchcom_func_reflash.c create mode 100644 tcm/synaptics_touchcom_func_reflash.h create mode 100644 tcm/synaptics_touchcom_func_romboot.c create mode 100644 tcm/synaptics_touchcom_func_romboot.h create mode 100644 tcm/synaptics_touchcom_func_touch.c create mode 100644 tcm/synaptics_touchcom_func_touch.h (limited to 'tcm') diff --git a/tcm/synaptics_touchcom_core_dev.h b/tcm/synaptics_touchcom_core_dev.h new file mode 100644 index 0000000..9ac2ee8 --- /dev/null +++ b/tcm/synaptics_touchcom_core_dev.h @@ -0,0 +1,1096 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file: synaptics_touchcom_core_dev.h + * + * This file is the topmost header file for Synaptics TouchComm device, also + * defines the TouchComm device context structure which will be passed to + * all other functions that expect a device handle. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ +#define _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ + + +#include "syna_tcm2_platform.h" + + +#define SYNA_TCM_CORE_LIB_VERSION 0x0111 + + +/** + * @section: Parameters pre-defined + * + * @brief: MAX_NUM_OBJECTS + * Maximum number of objects being detected + * + * @brief: MAX_SIZE_GESTURE_DATA + * Maximum size of gesture data + * + * @brief: MAX_SIZE_CONFIG_ID + * Maximum size of customer configuration ID + */ +#define MAX_NUM_OBJECTS (10) + +#define MAX_SIZE_GESTURE_DATA (8) + +#define MAX_SIZE_CONFIG_ID (16) + +/** + * @section: Command-handling relevant definitions + * + * @brief: MESSAGE_HEADER_SIZE + * The size of message header + * + * @brief: CMD_RESPONSE_TIMEOUT_MS + * Time frame for a command execution + * + * @brief: CMD_RESPONSE_POLLING_DELAY_MS + * Generic time frame to check the response in polling + * + * @brief: RD_RETRY_US + * For retry reading, delay time range in microsecond + * + * @brief: WR_DELAY_US + * For continued writes, delay time range in microsecond + * + * @brief: TAT_DELAY_US + * For bus turn-around, delay time range in microsecond + * + * @brief: FW_MODE_SWITCH_DELAY_MS + * The default time for fw mode switching + * + * @brief: RESET_DELAY_MS + * The default time after reset in case it's not set properly + * + * @brief: FORCE_ATTN_DRIVEN + * Special flag to read in resp packet in ISR function + */ +#define MESSAGE_HEADER_SIZE (4) + +#define CMD_RESPONSE_TIMEOUT_MS (3000) + +#define CMD_RESPONSE_POLLING_DELAY_MS (10) + +#define RD_RETRY_US_MIN (5000) +#define RD_RETRY_US_MAX (10000) + +#define WR_DELAY_US_MIN (500) +#define WR_DELAY_US_MAX (1000) + +#define TAT_DELAY_US_MIN (100) +#define TAT_DELAY_US_MAX (200) + +#define FW_MODE_SWITCH_DELAY_MS (200) + +#define RESET_DELAY_MS (200) + +#define DEFAULT_FLASH_ERASE_DELAY (~0) +#define DEFAULT_FLASH_WRITE_DELAY (~0) +#define DEFAULT_FLASH_READ_DELAY (~0) + +#define RESP_IN_ATTN (0) +#define RESP_IN_POLLING (CMD_RESPONSE_POLLING_DELAY_MS) + +/** + * @section: Macro to show string in log + */ +#define STR(x) #x + +/** + * @section: Helpers to check the device mode + */ +#define IS_APP_FW_MODE(mode) \ + (mode == MODE_APPLICATION_FIRMWARE) + +#define IS_NOT_APP_FW_MODE(mode) \ + (!IS_APP_FW_MODE(mode)) + +#define IS_BOOTLOADER_MODE(mode) \ + ((mode == MODE_BOOTLOADER) || \ + (mode == MODE_TDDI_BOOTLOADER) || \ + (mode == MODE_TDDI_HDL_BOOTLOADER) || \ + (mode == MODE_MULTICHIP_TDDI_BOOTLOADER)) + +#define IS_ROM_BOOTLOADER_MODE(mode) \ + (mode == MODE_ROMBOOTLOADER) + + +/** + * @section: Types for lower-level bus being used + */ +enum bus_connection { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_I3C, +}; + +/** + * @section: TouchComm Firmware Modes + * + * The current mode running is defined in Identify Info Packet. + */ +enum tcm_firmware_mode { + MODE_UNKNOWN = 0x00, + MODE_APPLICATION_FIRMWARE = 0x01, + MODE_HOSTDOWNLOAD_FIRMWARE = 0x02, + MODE_ROMBOOTLOADER = 0x04, + MODE_BOOTLOADER = 0x0b, + MODE_TDDI_BOOTLOADER = 0x0c, + MODE_TDDI_HDL_BOOTLOADER = 0x0d, + MODE_PRODUCTIONTEST_FIRMWARE = 0x0e, + MODE_MULTICHIP_TDDI_BOOTLOADER = 0xab, +}; + +/** + * @section: Status of Application Firmware + * + * The current status is defined in Application Info Packet. + */ +enum tcm_app_status { + APP_STATUS_OK = 0x00, + APP_STATUS_BOOTING = 0x01, + APP_STATUS_UPDATING = 0x02, + APP_STATUS_BAD_APP_CONFIG = 0xff, +}; + +/** + * @section: Field IDs in Dynamic Configuration + * + * The codes specify the generic dynamic configuration options. + */ +enum dynamic_tcm_config_id { + DC_UNKNOWN = 0x00, + DC_DISABLE_DOZE = 0x01, + DC_DISABLE_NOISE_MITIGATION = 0x02, + DC_DISABLE_FREQUENCY_SHIFT = 0x03, + DC_REQUEST_FREQUENCY_INDEX = 0x04, + DC_DISABLE_HSYNC = 0x05, + DC_REZERO_ON_EXIT_DEEP_SLEEP = 0x06, + DC_ENABLE_CHARGER_CONNECTED = 0x07, + DC_DISABLE_BASELINE_RELAXATION = 0x08, + DC_ENABLE_WAKEUP_GESTURE_MODE = 0x09, + DC_REQUEST_TESTING_FINGERS = 0x0a, + DC_ENABLE_GRIP_SUPPRESSION = 0x0b, + DC_ENABLE_THICK_GLOVE = 0x0c, + DC_ENABLE_GLOVE = 0x0d, + DC_ENABLE_FACE_DETECTION = 0x0e, + DC_INHIBIT_ACTIVE_GESTURE = 0x0f, + DC_DISABLE_PROXIMITY = 0x10, +}; + +/** + * @section: TouchComm Commands + * + * List the generic commands supported in TouchComm command-response protocol. + */ +enum tcm_command { + CMD_NONE = 0x00, + CMD_CONTINUE_WRITE = 0x01, + CMD_IDENTIFY = 0x02, + CMD_RESET = 0x04, + CMD_ENABLE_REPORT = 0x05, + CMD_DISABLE_REPORT = 0x06, + CMD_TCM2_ACK = 0x07, + CMD_TCM2_RETRY = 0x08, + CMD_TCM2_SET_MAX_READ_LENGTH = 0x09, + CMD_TCM2_GET_REPORT = 0x0a, + CMD_GET_BOOT_INFO = 0x10, + CMD_ERASE_FLASH = 0x11, + CMD_WRITE_FLASH = 0x12, + CMD_READ_FLASH = 0x13, + CMD_RUN_APPLICATION_FIRMWARE = 0x14, + CMD_SPI_MASTER_WRITE_THEN_READ = 0x15, + CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16, + CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f, + CMD_GET_APPLICATION_INFO = 0x20, + CMD_GET_STATIC_CONFIG = 0x21, + CMD_SET_STATIC_CONFIG = 0x22, + CMD_GET_DYNAMIC_CONFIG = 0x23, + CMD_SET_DYNAMIC_CONFIG = 0x24, + CMD_GET_TOUCH_REPORT_CONFIG = 0x25, + CMD_SET_TOUCH_REPORT_CONFIG = 0x26, + CMD_REZERO = 0x27, + CMD_COMMIT_CONFIG = 0x28, + CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29, + CMD_PRODUCTION_TEST = 0x2a, + CMD_SET_CONFIG_ID = 0x2b, + CMD_ENTER_DEEP_SLEEP = 0x2c, + CMD_EXIT_DEEP_SLEEP = 0x2d, + CMD_GET_TOUCH_INFO = 0x2e, + CMD_GET_DATA_LOCATION = 0x2f, + CMD_DOWNLOAD_CONFIG = 0x30, + CMD_ENTER_PRODUCTION_TEST_MODE = 0x31, + CMD_GET_FEATURES = 0x32, + CMD_GET_ROMBOOT_INFO = 0x40, + CMD_WRITE_PROGRAM_RAM = 0x41, + CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE = 0x42, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED = 0x43, + CMD_ENTER_IO_BRIDGE_MODE = 0x44, + CMD_ROMBOOT_DOWNLOAD = 0x45, +}; + +/** + * @section: TouchComm Status Codes + * + * Define the following status codes for all command responses. + * 0x00: (v1) no commands are pending and no reports are available. + * 0x01: (v1 & v2) the previous command succeeded. + * 0x03: (v1 & v2) the payload continues a previous response. + * 0x04: (v2) command was written, but no reports were available. + * 0x07: (v2) the previous write was successfully received. + * 0x08: (v2) the previous write was corrupt. The host should resend. + * 0x09: (v2) the previous command failed. + * 0x0c: (v1 & v2) write was larger than the device's receive buffer. + * 0x0d: (v1 & v2) a command was sent before the previous command completed. + * 0x0e: (v1 & v2) the requested command is not implemented. + * 0x0f: (v1 & v2) generic communication error, probably incorrect payload. + * + * 0xfe: self-defined status for a corrupted packet. + * 0xff: self-defined status for an invalid data. + */ +enum tcm_status_code { + STATUS_IDLE = 0x00, + STATUS_OK = 0x01, + STATUS_CONTINUED_READ = 0x03, + STATUS_NO_REPORT_AVAILABLE = 0x04, + STATUS_ACK = 0x07, + STATUS_RETRY_REQUESTED = 0x08, + STATUS_CMD_FAILED = 0x09, + STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, + STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, + STATUS_NOT_IMPLEMENTED = 0x0e, + STATUS_ERROR = 0x0f, + STATUS_PACKET_CORRUPTED = 0xfe, + STATUS_INVALID = 0xff, +}; + +/** + * @section: TouchComm Report Codes + * + * Define the following report codes generated by TouchComm firmware. + * 0x10: Identify Info Packet + * 0x11: Touch Report + * 0x12: Delta Cap. Image + * 0x13: Raw Cap. Image + */ +enum tcm_report_type { + REPORT_IDENTIFY = 0x10, + REPORT_TOUCH = 0x11, + REPORT_DELTA = 0x12, + REPORT_RAW = 0x13, +}; + +/** + * @section: States in Command Processing + * + * List the states in command processing. + */ +enum tcm_command_status { + CMD_STATE_IDLE = 0, + CMD_STATE_BUSY = 1, + CMD_STATE_ERROR = -1, +}; + +/** + * @section: Production Test Items + * + * List the generic production test items + */ +enum tcm_test_code { + TEST_NOT_IMPLEMENTED = 0x00, + + TEST_PID01_TRX_TRX_SHORTS = 0x01, + TEST_PID02_TRX_SENSOR_OPENS = 0x02, + TEST_PID03_TRX_GROUND_SHORTS = 0x03, + TEST_PID05_FULL_RAW_CAP = 0x05, + TEST_PID06_EE_SHORT = 0x06, + TEST_PID07_DYNAMIC_RANGE = 0x07, + TEST_PID08_HIGH_RESISTANCE = 0x08, + TEST_PID10_DELTA_NOISE = 0x0a, + TEST_PID11_OPEN_DETECTION = 0x0b, + TEST_PID12 = 0x0c, + TEST_PID13 = 0x0d, + TEST_PID14_DOZE_DYNAMIC_RANGE = 0x0e, + TEST_PID15_DOZE_NOISE = 0x0f, + TEST_PID16_SENSOR_SPEED = 0x10, + TEST_PID17_ADC_RANGE = 0x11, + TEST_PID18_HYBRID_ABS_RAW = 0x12, + TEST_PID29_HYBRID_ABS_NOISE = 0x1D, + + TEST_PID_MAX, +}; + + +/** + * @section: Internal Buffer Structure + * + * This structure is taken as the internal common buffer. + */ +struct tcm_buffer { + unsigned char *buf; + unsigned int buf_size; + unsigned int data_length; + syna_pal_mutex_t buf_mutex; + unsigned char ref_cnt; +}; + +/** + * @section: TouchComm Identify Info Packet + * Ver.1: size is 24 (0x18) bytes + * Ver.2: size is extended to 32 (0x20) bytes + * + * The identify packet provides the basic TouchComm information and indicate + * that the device is ready to receive commands. + * + * The report is received whenever the device initially powers up, resets, + * or switches fw between bootloader and application modes. + */ +struct tcm_identification_info { + unsigned char version; + unsigned char mode; + unsigned char part_number[16]; + unsigned char build_id[4]; + unsigned char max_write_size[2]; + /* extension in ver.2 */ + unsigned char max_read_size[2]; + unsigned char reserved[6]; +}; + + +/** + * @section: TouchComm Application Information Packet + * + * The application info packet provides the information about the application + * firmware as well as the touch controller. + */ +struct tcm_application_info { + unsigned char version[2]; + unsigned char status[2]; + unsigned char static_config_size[2]; + unsigned char dynamic_config_size[2]; + unsigned char app_config_start_write_block[2]; + unsigned char app_config_size[2]; + unsigned char max_touch_report_config_size[2]; + unsigned char max_touch_report_payload_size[2]; + unsigned char customer_config_id[MAX_SIZE_CONFIG_ID]; + unsigned char max_x[2]; + unsigned char max_y[2]; + unsigned char max_objects[2]; + unsigned char num_of_buttons[2]; + unsigned char num_of_image_rows[2]; + unsigned char num_of_image_cols[2]; + unsigned char has_hybrid_data[2]; + unsigned char num_of_force_elecs[2]; +}; + +/** + * @section: TouchComm boot information packet + * + * The boot info packet provides the information of TouchBoot. + */ +struct tcm_boot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char erase_page_size_words[2]; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; + unsigned char boot_config_start_block[2]; + unsigned char boot_config_size_blocks[2]; + /* extension in ver.2 */ + unsigned char display_config_start_block[4]; + unsigned char display_config_length_blocks[2]; + unsigned char backup_display_config_start_block[4]; + unsigned char backup_display_config_length_blocks[2]; + unsigned char custom_otp_start_block[2]; + unsigned char custom_otp_length_blocks[2]; +}; + +/** + * @section: TouchComm ROMboot information packet + * + * The ROMboot info packet provides the information of ROM bootloader. + */ +struct tcm_romboot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; +}; + +/** + * @section: TouchComm features description packet + * + * The features description packet tells which features are supported. + */ +struct tcm_features_info { + unsigned char byte[16]; +}; + +/** + * @section: Data blob for touch data reported + * + * Once receiving a touch report generated by firmware, the touched data + * will be parsed and converted to touch_data_blob structure as a data blob. + * + * @subsection: tcm_touch_data_blob + * The touch_data_blob contains all sorts of touched data entities. + * + * @subsection tcm_objects_data_blob + * The objects_data_blob includes the data for each active objects. + * + * @subsection tcm_gesture_data_blob + * The gesture_data_blob contains the gesture data if detected. + */ +struct tcm_objects_data_blob { + unsigned char status; + unsigned int x_pos; + unsigned int y_pos; + unsigned int x_width; + unsigned int y_width; + unsigned int z; + unsigned int tx_pos; + unsigned int rx_pos; +}; +struct tcm_gesture_data_blob { + union { + struct { + unsigned char tap_x[2]; + unsigned char tap_y[2]; + }; + struct { + unsigned char swipe_x[2]; + unsigned char swipe_y[2]; + unsigned char swipe_direction[2]; + }; + unsigned char data[MAX_SIZE_GESTURE_DATA]; + }; +}; +struct tcm_touch_data_blob { + + /* for each active objects */ + unsigned int num_of_active_objects; + struct tcm_objects_data_blob object_data[MAX_NUM_OBJECTS]; + + /* for gesture */ + unsigned int gesture_id; + struct tcm_gesture_data_blob gesture_data; + + /* various data */ + unsigned int timestamp; + unsigned int buttons_state; + unsigned int frame_rate; + unsigned int power_im; + unsigned int cid_im; + unsigned int rail_im; + unsigned int cid_variance_im; + unsigned int nsm_frequency; + unsigned int nsm_state; + unsigned int num_of_cpu_cycles; + unsigned int fd_data; + unsigned int force_data; + unsigned int fingerprint_area_meet; + unsigned int sensing_mode; +}; + +/** + * @section: Callback function used for custom entity parsing in touch report + * + * Allow to implement the custom handling for"new" custom entity in touch report + * + * @param + * [ in] code: the code of current touch entity + * [ in] config: the report configuration stored + * [in/out] config_offset: offset of current position in report config, + * the updated position should be returned. + * [ in] report: touch report given + * [in/out] report_offset: offset of current position in touch report, + * the updated position should be returned. + * [ in] report_size: size of given touch report + * [ in] callback_data: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +typedef int (*tcm_touch_data_parse_callback_t) (const unsigned char code, + const unsigned char *config, unsigned int *config_offset, + const unsigned char *report, unsigned int *report_offset, + unsigned int report_size, void *callback_data); + +/** + * @section: Callback function used for custom gesture parsing in touch report + * + * Allow to implement the custom handling for gesture data. + * + * @param + * [ in] code: the code of current touch entity + * [ in] config: the report configuration stored + * [in/out] config_offset: offset of current position in report config, + * the updated position should be returned. + * [ in] report: touch report given + * [in/out] report_offset: offset of current position in touch report, + * the updated position should be returned. + * [ in] report_size: size of given touch report + * [ in] callback_data: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +typedef int (*tcm_gesture_parse_callback_t) (const unsigned char code, + const unsigned char *config, unsigned int *config_offset, + const unsigned char *report, unsigned int *report_offset, + unsigned int report_size, void *callback_data); + +/** + * @section: TouchComm Message Handling Wrapper + * + * The structure contains the essential buffers and parameters to implement + * the command-response protocol for both TouchCom ver.1 and TouchCom ver.2. + */ +struct tcm_message_data_blob { + + /* parameters for command processing */ + syna_pal_atomic_t command_status; + unsigned char command; + unsigned char status_report_code; + unsigned int payload_length; + unsigned char response_code; + unsigned char report_code; + unsigned char seq_toggle; + unsigned int default_resp_reading; + + /* completion event command processing */ + syna_pal_completion_t cmd_completion; + + /* internal buffers + * in : buffer storing the data being read 'in' + * out : buffer storing the data being sent 'out' + * temp: 'temp' buffer used for continued read operation + */ + struct tcm_buffer in; + struct tcm_buffer out; + struct tcm_buffer temp; + + /* mutex for the protection of command processing */ + syna_pal_mutex_t cmd_mutex; + + /* mutex for the read/write protection */ + syna_pal_mutex_t rw_mutex; + +}; + +/** + * @section: TouchComm core device context structure + * + * The device context contains parameters and internal buffers, that will + * be passed to all other functions that expect a device handle. + * + * Calling syna_tcm_allocate_device() can allocate this structure, and + * syna_tcm_remove_device() releases the structure if no longer needed. + */ +struct tcm_dev { + + /* basic device information */ + unsigned char dev_mode; + unsigned int packrat_number; + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + unsigned int rows; + unsigned int cols; + unsigned char config_id[MAX_SIZE_CONFIG_ID]; + + /* capability of read/write transferred + * being assigned through syna_hw_interface + */ + unsigned int max_wr_size; + unsigned int max_rd_size; + + /* hardware-specific data structure + * defined in syna_touchcom_platform.h + */ + struct syna_hw_interface *hw_if; + + /* TouchComm defined structures */ + struct tcm_identification_info id_info; + struct tcm_application_info app_info; + struct tcm_boot_info boot_info; + + /* internal buffers + * report: record the TouchComm report to caller + * resp : record the command response to caller + */ + struct tcm_buffer report_buf; + struct tcm_buffer resp_buf; + struct tcm_buffer external_buf; + + /* touch report configuration */ + struct tcm_buffer touch_config; + + /* TouchComm message handling wrapper */ + struct tcm_message_data_blob msg_data; + + /* indicate that fw update is on-going */ + syna_pal_atomic_t firmware_flashing; + + /* abstraction to read a TouchComm message from device. + * Function will be assigned by syna_tcm_detect_device(). + * + * After read_message() returned, the retrieved data will be available + * and stored either in buffer.report or buffer.resp based on the + * code returned. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*read_message)(struct tcm_dev *tcm_dev, + unsigned char *status_report_code); + + /* abstraction to write a TouchComm message to device and retrieve the + * response to command. + * Function will be assigned by syna_tcm_detect_device(). + * + * After calling write_message(), the response code is returned + * and the response data is stored in buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command to write + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the polling time; + * or, set '0' (RESP_IN_ATTN) for ATTN driven + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*write_message)(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + unsigned int delay_ms_resp); + + + /* callbacks + * custom_touch_data_parse_func: custom touch data entity parsing + * custom_gesture_parse_func : custom gesture data entity parsing + */ + tcm_touch_data_parse_callback_t custom_touch_data_parse_func; + void *cbdata_touch_data_parse; + tcm_gesture_parse_callback_t custom_gesture_parse_func; + void *cbdata_gesture_parse; + +}; +/* end of structure syna_tcm_dev */ + + +/** + * @section: Protocol detection + * + * @brief: syna_tcm_v1_detect + * Check whether TouchComm ver.1 firmware is running + * + * @brief: syna_tcm_v2_detect + * Check whether TouchComm ver.2 firmware is running + */ + +/* syna_tcm_v1_detect() + * + * Check whether TouchComm ver.1 firmware is running. + * Function is implemented in synaptics_tcm2_core_v1.c. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int data_len); + +/* syna_tcm_v2_detect() + * + * Check whether TouchComm ver.2 firmware is running. + * Function is implemented in synaptics_tcm2_core_v2.c. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int data_len); + +/** + * @section: Buffers Management helpers + * + * @brief: syna_tcm_buf_alloc + * Allocate the requested memory space for the buffer structure + * + * @brief: syna_tcm_buf_realloc + * Extend the requested memory space for the buffer structure + * + * @brief: syna_tcm_buf_init + * Initialize the buffer structure + * + * @brief: syna_tcm_buf_lock + * Protect the access of current buffer structure + * + * @brief: syna_tcm_buf_unlock + * Open the access of current buffer structure + * + * @brief: syna_tcm_buf_release + * Release the buffer structure + */ + +/** + * syna_tcm_buf_alloc() + * + * Allocate the requested memory space for the given buffer only if + * the existed buffer is not enough for the requirement. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * [ in] size: required size to be allocated + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_alloc(struct tcm_buffer *pbuf, + unsigned int size) +{ + if (!pbuf) { + LOGE("Invalid buffer structure\n"); + return -1; + } + + if (size > pbuf->buf_size) { + if (pbuf->buf) + syna_pal_mem_free((void *)pbuf->buf); + + pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!(pbuf->buf)) { + LOGE("Fail to allocate memory (size = %d)\n", + (int)(size*sizeof(unsigned char))); + pbuf->buf_size = 0; + pbuf->data_length = 0; + return -1; + } + pbuf->buf_size = size; + } + + syna_pal_mem_set(pbuf->buf, 0x00, pbuf->buf_size); + pbuf->data_length = 0; + + return 0; +} + +/** + * syna_tcm_buf_realloc() + * + * Extend the requested memory space for the given buffer only if + * the existed buffer is not enough for the requirement. + * Then, move the content to the new memory space. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * [ in] size: required size to be extended + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_realloc(struct tcm_buffer *pbuf, + unsigned int size) +{ + int retval; + unsigned char *temp_src; + unsigned int temp_size = 0; + + if (!pbuf) { + LOGE("Invalid buffer structure\n"); + return -1; + } + + if (size > pbuf->buf_size) { + temp_src = pbuf->buf; + temp_size = pbuf->buf_size; + + pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!(pbuf->buf)) { + LOGE("Fail to allocate memory (size = %d)\n", + (int)(size * sizeof(unsigned char))); + syna_pal_mem_free((void *)temp_src); + pbuf->buf_size = 0; + return -1; + } + + retval = syna_pal_mem_cpy(pbuf->buf, + size, + temp_src, + temp_size, + temp_size); + if (retval < 0) { + LOGE("Fail to copy data\n"); + syna_pal_mem_free((void *)temp_src); + syna_pal_mem_free((void *)pbuf->buf); + pbuf->buf_size = 0; + return retval; + } + + syna_pal_mem_free((void *)temp_src); + pbuf->buf_size = size; + } + + return 0; +} +/** + * syna_tcm_buf_init() + * + * Initialize the buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_init(struct tcm_buffer *pbuf) +{ + pbuf->buf_size = 0; + pbuf->data_length = 0; + pbuf->ref_cnt = 0; + pbuf->buf = NULL; + syna_pal_mutex_alloc(&pbuf->buf_mutex); +} +/** + * syna_tcm_buf_lock() + * + * Protect the access of current buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_lock(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 0) { + LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt); + return; + } + + syna_pal_mutex_lock(&pbuf->buf_mutex); + pbuf->ref_cnt++; +} +/** + * syna_tcm_buf_unlock() + * + * Open the access of current buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_unlock(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 1) { + LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt); + return; + } + + pbuf->ref_cnt--; + syna_pal_mutex_unlock(&pbuf->buf_mutex); +} +/** + * syna_tcm_buf_release() + * + * Release the buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_release(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 0) + LOGE("Buffer access hold, %d\n", pbuf->ref_cnt); + + syna_pal_mutex_free(&pbuf->buf_mutex); + syna_pal_mem_free((void *)pbuf->buf); + pbuf->buf_size = 0; + pbuf->data_length = 0; + pbuf->ref_cnt = 0; +} +/** + * syna_tcm_buf_copy() + * + * Helper to copy data from the source buffer to the destination buffer. + * The size of destination buffer may be reallocated, if the size is + * smaller than the actual data size to be copied. + * + * @param + * [out] dest: pointer to an internal buffer + * [ in] src: required size to be extended + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_copy(struct tcm_buffer *dest, + struct tcm_buffer *src) +{ + int retval = 0; + + if (dest->buf_size < src->data_length) { + retval = syna_tcm_buf_alloc(dest, src->data_length + 1); + if (retval < 0) { + LOGE("Fail to reallocate the given buffer, size: %d\n", + src->data_length + 1); + return retval; + } + } + + /* copy data content to the destination */ + retval = syna_pal_mem_cpy(dest->buf, + dest->buf_size, + src->buf, + src->buf_size, + src->data_length); + if (retval < 0) { + LOGE("Fail to copy data to caller, size: %d\n", + src->data_length); + return retval; + } + + dest->data_length = src->data_length; + + return retval; +} +/** + * @section: Reads / Writes Abstraction Function + * + * @brief: syna_tcm_read + * Read the data from bus directly + * + * @brief: syna_tcm_write + * Write the data to bus directly + */ + +/** + * syna_tcm_read() + * + * The bare read function, reading in the requested data bytes + * from bus directly. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rd_data: buffer for storing data retrieved from device + * [ in] rd_len: length of reading data in bytes + * + * @return + * the number of data bytes retrieved; + * otherwise, negative value, on error. + */ +static inline int syna_tcm_read(struct tcm_dev *tcm_dev, + unsigned char *rd_data, unsigned int rd_len) +{ + struct syna_hw_interface *hw_if; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + hw_if = tcm_dev->hw_if; + if (!hw_if->ops_read_data) { + LOGE("Invalid hw ops_read function\n"); + return _ENODEV; + } + + return hw_if->ops_read_data(hw_if, rd_data, rd_len); +} + +/** + * syna_tcm_write() + * + * The bare write function, writing the given data bytes to bus directly. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] wr_data: written data + * [ in] wr_len: length of written data in bytes + * + * @return + * the number of data bytes retrieved; + * otherwise, negative value, on error. + */ +static inline int syna_tcm_write(struct tcm_dev *tcm_dev, + unsigned char *wr_data, unsigned int wr_len) +{ + struct syna_hw_interface *hw_if; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + hw_if = tcm_dev->hw_if; + if (!hw_if->ops_write_data) { + LOGE("Invalid hw ops_write function\n"); + return _ENODEV; + } + + return hw_if->ops_write_data(hw_if, wr_data, wr_len); +} + + +#endif /* end of _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ */ diff --git a/tcm/synaptics_touchcom_core_v1.c b/tcm/synaptics_touchcom_core_v1.c new file mode 100644 index 0000000..d10380f --- /dev/null +++ b/tcm/synaptics_touchcom_core_v1.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_core_v1.c + * + * This file implements the TouchComm version 1 command-response protocol. + */ + +#include "synaptics_touchcom_core_dev.h" + +#define TCM_V1_MESSAGE_MARKER 0xa5 +#define TCM_V1_MESSAGE_PADDING 0x5a + +/** + * @section: Header of TouchComm v1 Message Packet + * + * The 4-byte header in the TouchComm v1 packet + */ +struct tcm_v1_message_header { + union { + struct { + unsigned char marker; + unsigned char code; + unsigned char length[2]; + }; + unsigned char data[MESSAGE_HEADER_SIZE]; + }; +}; + +/** + * syna_tcm_v1_parse_idinfo() + * + * Copy the given data to the identification info structure + * and parse the basic information, e.g. fw build id. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: data buffer + * [ in] size: size of given data buffer + * [ in] data_len: length of actual data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_parse_idinfo(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int size, unsigned int data_len) +{ + int retval; + unsigned int wr_size = 0; + unsigned int build_id = 0; + struct tcm_identification_info *id_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_len == 0)) { + LOGE("Invalid given data buffer\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + data, + size, + MIN(sizeof(*id_info), data_len)); + if (retval < 0) { + LOGE("Fail to copy identification info\n"); + return retval; + } + + build_id = syna_pal_le4_to_uint(id_info->build_id); + + wr_size = syna_pal_le2_to_uint(id_info->max_write_size); + tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE); + if (tcm_dev->max_wr_size == 0) { + tcm_dev->max_wr_size = wr_size; + LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size); + } + + LOGI("TCM Fw mode: 0x%02x\n", id_info->mode); + + if (tcm_dev->packrat_number != build_id) + tcm_dev->packrat_number = build_id; + + tcm_dev->dev_mode = id_info->mode; + + return 0; +} + +/** + * syna_tcm_v1_dispatch_report() + * + * Handle the TouchCom report packet being received. + * + * If it's an identify report, parse the identification packet and signal + * the command completion just in case. + * Otherwise, copy the data from internal buffer.in to internal buffer.report + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v1_dispatch_report(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->report_code = tcm_msg->status_report_code; + + if (tcm_msg->payload_length == 0) { + tcm_dev->report_buf.data_length = 0; + goto exit; + } + + /* The identify report may be resulted from reset or fw mode switching + */ + if (tcm_msg->report_code == REPORT_IDENTIFY) { + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v1_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* in case, the identify info packet is caused by the command */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + switch (tcm_msg->command) { + case CMD_RESET: + LOGD("Reset by CMD_RESET\n"); + case CMD_REBOOT_TO_ROM_BOOTLOADER: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE: + tcm_msg->status_report_code = STATUS_OK; + tcm_msg->response_code = STATUS_OK; + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_IDLE); + syna_pal_completion_complete(cmd_completion); + goto exit; + default: + LOGN("Device has been reset\n"); + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + goto exit; + } + } + } + + /* store the received report into the internal buffer.report */ + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->report_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.report\n"); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf, + tcm_dev->report_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to buf_report\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + tcm_dev->report_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + +exit: + return; +} + +/** + * syna_tcm_v1_dispatch_response() + * + * Handle the response packet. + * + * Copy the data from internal buffer.in to internal buffer.resp, + * and then signal the command completion. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v1_dispatch_response(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->response_code = tcm_msg->status_report_code; + + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY) + return; + + if (tcm_msg->payload_length == 0) { + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* copy the received resp data into the internal buffer.resp */ + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.resp\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal resp_buf\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + +exit: + syna_pal_completion_complete(cmd_completion); +} + + +/** + * syna_tcm_v1_read() + * + * Read in a TouchCom packet from device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] rd_length: number of reading bytes; + * '0' means to read the message header only + * [in/out] buf: pointer to a buffer which is stored the retrieved data + * [out] buf_size: size of the buffer pointed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_read(struct tcm_dev *tcm_dev, unsigned int rd_length, + unsigned char *buf, unsigned int buf_size) +{ + int retval; + unsigned int max_rd_size; + int retry; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (rd_length == 0) + return 0; + + if (rd_length > buf_size) { + LOGE("Invalid read length, len: %d, buf_size: %d\n", + rd_length, buf_size); + return _EINVAL; + } + + max_rd_size = tcm_dev->max_rd_size; + + if ((max_rd_size != 0) && (rd_length > max_rd_size)) { + LOGE("Invalid read length, len: %d, max_rd_size: %d\n", + rd_length, max_rd_size); + return _EINVAL; + } + + /* read in the message header from device + * will do retry if the packet is not expected + */ + for (retry = 0; retry < 10; retry++) { + retval = syna_tcm_read(tcm_dev, + buf, + rd_length + ); + if (retval < 0) { + LOGE("Fail to read %d bytes to device\n", rd_length); + goto exit; + } + + /* check the message header */ + if (buf[0] == TCM_V1_MESSAGE_MARKER) + break; + + LOGE("Incorrect header marker, 0x%02x (retry:%d)\n", + buf[0], retry); + + retval = _EIO; + syna_pal_sleep_us(RD_RETRY_US_MIN, RD_RETRY_US_MAX); + } + +exit: + return retval; +} + +/** + * syna_tcm_v1_write() + * + * Construct the TouchCom v1 packet and send it to device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if have any + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_write(struct tcm_dev *tcm_dev, unsigned char command, + unsigned char *payload, unsigned int payload_len) +{ + int retval = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + int size; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + syna_tcm_buf_lock(&tcm_msg->out); + + /* allocate the space storing the written data */ + retval = syna_tcm_buf_alloc(&tcm_msg->out, payload_len + 3); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.out\n"); + goto exit; + } + + if (command != CMD_CONTINUE_WRITE) { + /* construct the command packet + * size = 1-byte command + 2-byte length + payload + */ + size = payload_len + 3; + + tcm_msg->out.buf[0] = command; + tcm_msg->out.buf[1] = (unsigned char)payload_len; + tcm_msg->out.buf[2] = (unsigned char)(payload_len >> 8); + + if (payload_len > 0) { + retval = syna_pal_mem_cpy(&tcm_msg->out.buf[3], + tcm_msg->out.buf_size - 3, + payload, + payload_len, + payload_len + ); + if (retval < 0) { + LOGE("Fail to copy payload\n"); + goto exit; + } + } + } else { + /* construct the continued writes packet + * size = 1-byte continued write + payload + */ + size = payload_len + 1; + + tcm_msg->out.buf[0] = CMD_CONTINUE_WRITE; + + retval = syna_pal_mem_cpy(&tcm_msg->out.buf[1], + tcm_msg->out.buf_size - 1, + payload, + payload_len, + payload_len + ); + if (retval < 0) { + LOGE("Fail to copy continued write\n"); + goto exit; + } + } + + /* write command packet to the device */ + retval = syna_tcm_write(tcm_dev, + tcm_msg->out.buf, + size + ); + if (retval < 0) { + LOGE("Fail to write %d bytes to device\n", size); + goto exit; + } + +exit: + syna_tcm_buf_unlock(&tcm_msg->out); + + return retval; +} + +/** + * syna_tcm_v1_continued_read() + * + * The remaining data payload is read in continuously until the end of data. + * All the retrieved data is appended to the internal buffer.in. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: length of payload data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_continued_read(struct tcm_dev *tcm_dev, + unsigned int length) +{ + int retval = 0; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + /* continued read packet contains the header, payload, and a padding */ + total_length = MESSAGE_HEADER_SIZE + length + 1; + remaining_length = total_length - MESSAGE_HEADER_SIZE; + + syna_tcm_buf_lock(&tcm_msg->in); + + /* in case the current buf.in is smaller than requested size */ + retval = syna_tcm_buf_realloc(&tcm_msg->in, + total_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + /* available chunk space for payload = + * total chunk size - (header marker byte + header status byte) + */ + if (tcm_dev->max_rd_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_dev->max_rd_size - 2; + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + offset = MESSAGE_HEADER_SIZE; + + syna_tcm_buf_lock(&tcm_msg->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (xfer_length == 1) { + tcm_msg->in.buf[offset] = TCM_V1_MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + /* allocate the internal temp buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->temp, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.temp\n"); + goto exit; + } + /* retrieve data from the bus + * data should include header marker and status code + */ + retval = syna_tcm_v1_read(tcm_dev, + xfer_length + 2, + tcm_msg->temp.buf, + tcm_msg->temp.buf_size + ); + if (retval < 0) { + LOGE("Fail to read %d bytes from device\n", + xfer_length + 2); + goto exit; + } + + /* check the data content */ + code = tcm_msg->temp.buf[1]; + + if (code != STATUS_CONTINUED_READ) { + LOGE("Incorrect status code 0x%02x at %d out of %d\n", + code, idx, chunks); + retval = _EIO; + goto exit; + } + + /* copy data from internal buffer.temp to buffer.in */ + retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset], + tcm_msg->in.buf_size - offset, + &tcm_msg->temp.buf[2], + tcm_msg->temp.buf_size - 2, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy payload\n"); + goto exit; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + +exit: + syna_tcm_buf_unlock(&tcm_msg->temp); + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v1_read_message() + * + * Read in a TouchCom packet from device. + * The packet including its payload is read in from device and stored in + * the internal buffer.resp or buffer.report based on the code received. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v1_read_message(struct tcm_dev *tcm_dev, + unsigned char *status_report_code) +{ + int retval = 0; + struct tcm_v1_message_header *header; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (status_report_code) + *status_report_code = STATUS_INVALID; + + syna_pal_mutex_lock(rw_mutex); + + syna_tcm_buf_lock(&tcm_msg->in); + + /* read in the message header from device */ + retval = syna_tcm_v1_read(tcm_dev, + MESSAGE_HEADER_SIZE, + tcm_msg->in.buf, + tcm_msg->in.buf_size + ); + if (retval < 0) { + LOGE("Fail to read message header from device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + + tcm_msg->status_report_code = STATUS_INVALID; + tcm_msg->payload_length = 0; + goto exit; + } + + /* check the message header */ + header = (struct tcm_v1_message_header *)tcm_msg->in.buf; + + tcm_msg->status_report_code = header->code; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + + if (tcm_msg->status_report_code != STATUS_IDLE) + LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n", + tcm_msg->status_report_code, tcm_msg->payload_length, + header->data[0], header->data[1], header->data[2], + header->data[3]); + + syna_tcm_buf_unlock(&tcm_msg->in); + + if ((tcm_msg->status_report_code <= STATUS_ERROR) || + (tcm_msg->status_report_code == STATUS_INVALID)) { + switch (tcm_msg->status_report_code) { + case STATUS_OK: + break; + case STATUS_CONTINUED_READ: + LOGE("Out-of-sync continued read\n"); + case STATUS_IDLE: + tcm_msg->payload_length = 0; + retval = 0; + goto exit; + default: + LOGE("Incorrect Status code, 0x%02x\n", + tcm_msg->status_report_code); + tcm_msg->payload_length = 0; + goto do_dispatch; + } + } + + if (tcm_msg->payload_length == 0) + goto do_dispatch; + + /* retrieve the remaining data, if any */ + retval = syna_tcm_v1_continued_read(tcm_dev, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to do continued read\n"); + goto exit; + } + + /* refill the header for dispatching */ + syna_tcm_buf_lock(&tcm_msg->in); + + tcm_msg->in.buf[0] = TCM_V1_MESSAGE_MARKER; + tcm_msg->in.buf[1] = tcm_msg->status_report_code; + tcm_msg->in.buf[2] = (unsigned char)tcm_msg->payload_length; + tcm_msg->in.buf[3] = (unsigned char)(tcm_msg->payload_length >> 8); + + syna_tcm_buf_unlock(&tcm_msg->in); + +do_dispatch: + /* duplicate the data to external buffer */ + syna_tcm_buf_lock(&tcm_dev->external_buf); + if (tcm_msg->payload_length > 0) { + retval = syna_tcm_buf_alloc(&tcm_dev->external_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory, external_buf invalid\n"); + } else { + retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0], + tcm_msg->payload_length, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) + LOGE("Fail to copy data to external buffer\n"); + } + } + tcm_dev->external_buf.data_length = tcm_msg->payload_length; + syna_tcm_buf_unlock(&tcm_dev->external_buf); + + /* process the retrieved packet */ + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + syna_tcm_v1_dispatch_report(tcm_dev); + else + syna_tcm_v1_dispatch_response(tcm_dev); + + /* copy the status report code to caller */ + if (status_report_code) + *status_report_code = tcm_msg->status_report_code; + + retval = 0; + +exit: + /* raise the completion event when errors out */ + if (retval < 0) { + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + } + } + + syna_pal_mutex_unlock(rw_mutex); + + return retval; +} + +/** + * syna_tcm_v1_write_message() + * + * Write message including command and its payload to TouchCom device. + * Then, the response of the command generated by the device will be + * read in and stored in internal buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_len: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v1_write_message(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_len, unsigned char *resp_code, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + int timeout = 0; + int polling_ms = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *cmd_mutex = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + bool has_irq_ctrl = false; + bool in_polling = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_mutex = &tcm_msg->cmd_mutex; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (resp_code) + *resp_code = STATUS_INVALID; + + /* indicate which mode is used */ + in_polling = (delay_ms_resp != RESP_IN_ATTN); + + /* irq control is enabled only when the operations is implemented + * and the current status of irq is enabled. + * do not enable irq if it is disabled by someone. + */ + has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL); + has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled; + + /* disable irq when using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false); + + syna_pal_mutex_lock(cmd_mutex); + + syna_pal_mutex_lock(rw_mutex); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* reset the command completion */ + syna_pal_completion_reset(cmd_completion); + + tcm_msg->command = command; + + remaining_length = payload_len; + + /* available space for payload = total size - command byte */ + if (tcm_dev->max_wr_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_dev->max_wr_size - 1; + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + LOGD("Command: 0x%02x, payload len: %d\n", command, payload_len); + + /* send out command packets + * + * separate into several sub-packets if the overall size is over + * than the maximum write size. + */ + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (idx == 0) { + retval = syna_tcm_v1_write(tcm_dev, + tcm_msg->command, + &payload[0], + xfer_length); + } else { + retval = syna_tcm_v1_write(tcm_dev, + CMD_CONTINUE_WRITE, + &payload[idx * chunk_space], + xfer_length); + } + + if (retval < 0) { + LOGE("Fail to write %d bytes to device\n", + xfer_length); + syna_pal_mutex_unlock(rw_mutex); + goto exit; + } + + remaining_length -= xfer_length; + + if (chunks > 1) + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + } + + syna_pal_mutex_unlock(rw_mutex); + + /* handle the command response + * + * assuming to select the polling mode, the while-loop below will + * repeatedly read in the respose data based on the given polling + * time; otherwise, wait until receiving a completion event from + * interupt thread. + */ + timeout = 0; + if (!in_polling) + polling_ms = CMD_RESPONSE_TIMEOUT_MS; + else + polling_ms = delay_ms_resp; + + do { + /* wait for the completion event triggered by read_message */ + retval = syna_pal_completion_wait_for(cmd_completion, + polling_ms); + /* reset the status when times out and keep in polling */ + if (retval < 0) + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* break when geting a valid resp; otherwise, keep in polling */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE) + goto check_response; + + if (in_polling) { + /* retrieve the message packet back */ + retval = syna_tcm_v1_read_message(tcm_dev, NULL); + /* keep in polling if still not having a valid resp */ + if (retval < 0) + syna_pal_completion_reset(cmd_completion); + } + + timeout += polling_ms + 10; + + } while (timeout < CMD_RESPONSE_TIMEOUT_MS); + + /* check the status of response data + * according to the touchcomm spec, each command message + * should have an associated reponse message. + */ +check_response: + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) { + if (timeout >= CMD_RESPONSE_TIMEOUT_MS) { + LOGE("Timed out wait for response of command 0x%02x\n", + command); + retval = _ETIMEDOUT; + goto exit; + } else { + LOGE("Fail to get valid response of command 0x%02x\n", + command); + retval = _EIO; + goto exit; + } + } + + /* copy response code to the caller */ + if (resp_code) + *resp_code = tcm_msg->response_code; + + if (tcm_msg->response_code != STATUS_OK) { + LOGE("Error code 0x%02x of command 0x%02x\n", + tcm_msg->response_code, tcm_msg->command); + retval = _EIO; + } else { + retval = 0; + } + +exit: + tcm_msg->command = CMD_NONE; + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + syna_pal_mutex_unlock(cmd_mutex); + + /* recovery the irq if using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true); + + return retval; +} + +/** + * syna_tcm_v1_detect() + * + * For TouchCom v1 protocol, the given raw data must start with a specific + * maker code. If so, read the remaining packet from TouchCom device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int size) +{ + int retval; + struct tcm_v1_message_header *header; + struct tcm_message_data_blob *tcm_msg = NULL; + unsigned int payload_length = 0; + unsigned char resp_code = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (size < MESSAGE_HEADER_SIZE)) { + LOGE("Invalid parameters\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + header = (struct tcm_v1_message_header *)data; + + if (header->marker != TCM_V1_MESSAGE_MARKER) + return _ENODEV; + + /* after initially powering on, the identify report should be the + * first packet + */ + if (header->code == REPORT_IDENTIFY) { + + payload_length = syna_pal_le2_to_uint(header->length); + + /* retrieve the identify info packet */ + retval = syna_tcm_v1_continued_read(tcm_dev, + payload_length); + if (retval < 0) { + LOGE("Fail to read in identify info packet\n"); + return retval; + } + } else { + /* if not, send an identify command instead */ + retval = syna_tcm_v1_write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + RESP_IN_POLLING); + if (retval < 0) { + /* in case the identify command is not working, + * send a rest command as the workaround + */ + retval = syna_tcm_v1_write_message(tcm_dev, + CMD_RESET, + NULL, + 0, + &resp_code, + RESET_DELAY_MS); + if (retval < 0) { + LOGE("Fail to identify the device\n"); + return _ENODEV; + } + } + + payload_length = tcm_msg->payload_length; + } + + /* parse the identify info packet */ + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v1_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* expose the read / write operations */ + tcm_dev->read_message = syna_tcm_v1_read_message; + tcm_dev->write_message = syna_tcm_v1_write_message; + + return retval; +} diff --git a/tcm/synaptics_touchcom_core_v2.c b/tcm/synaptics_touchcom_core_v2.c new file mode 100644 index 0000000..c3f0d56 --- /dev/null +++ b/tcm/synaptics_touchcom_core_v2.c @@ -0,0 +1,1445 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_core_v2.c + * + * This file implements the TouchComm version 2 command-response protocol + */ + +#include "synaptics_touchcom_core_dev.h" + +#define BITS_IN_MESSAGE_HEADER (MESSAGE_HEADER_SIZE * 8) + +#define HOST_PRIMARY (0) + +#define COMMAND_RETRY_TIMES (5) + +#define CHECK_PACKET_CRC + +/** + * @section: Header of TouchComm v2 Message Packet + * + * The 4-byte header in the TouchComm v2 packet + */ +struct tcm_v2_message_header { + union { + struct { + unsigned char code; + unsigned char length[2]; + unsigned char byte3; + }; + unsigned char data[MESSAGE_HEADER_SIZE]; + }; +}; + +/* helper to execute a tcm v2 command + */ +static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length); + +/** + * @section: Lookup table for checksum calculation + * + * @subsection: crc6_table + * lookup table for crc6 calculation + * + * @subsection: crc16_table + * lookup table for crc16 calculation + */ +static unsigned short crc6_table[16] = { + 0, 268, 536, 788, 1072, 1340, 1576, 1828, + 2144, 2412, 2680, 2932, 3152, 3420, 3656, 3908 +}; + +static unsigned short crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +/** + * syna_tcm_v2_crc6() + * + * Calculate the crc-6 with polynomial for TouchCom v2 header. + * + * @param + * [ in] p: byte array for the calculation + * [ in] bits: number of bits + * + * @return + * the crc-6 value + */ +static unsigned char syna_tcm_v2_crc6(unsigned char *p, unsigned int bits) +{ + unsigned short r = 0x003F << 2; + unsigned short x; + + for (; bits > 8; bits -= 8) { + r ^= *p++; + r = (r << 4) ^ crc6_table[r >> 4]; + r = (r << 4) ^ crc6_table[r >> 4]; + } + + if (bits > 0) { + x = *p; + while (bits--) { + if (x & 0x80) + r ^= 0x80; + + x <<= 1; + r <<= 1; + if (r & 0x100) + r ^= (0x03 << 2); + } + } + + return (unsigned char)((r >> 2) & 0x3F); +} +/** + * syna_tcm_v2_crc6() + * + * Calculate the crc-16 for TouchCom v2 packet. + * + * @param + * [ in] p: byte array for the calculation + * [ in] len: length in bytes + * + * @return + * the crc-16 value + */ +static unsigned short syna_tcm_v2_crc16(unsigned char *p, unsigned int len) +{ + unsigned short r = 0xFFFF; + + while (len--) + r = (r << 8) ^ crc16_table[(r >> 8) ^ *p++]; + + return r; +} + +/** + * syna_tcm_v2_set_max_read_size() + * + * Configure the max length for message reading. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static int syna_tcm_v2_set_max_read_size(struct tcm_dev *tcm_dev) +{ + int retval; + unsigned int rd_size; + struct tcm_identification_info *id_info; + unsigned char data[2] = { 0 }; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + rd_size = syna_pal_le2_to_uint(id_info->max_read_size); + + if (rd_size == 0) { + LOGE("Invalid max_read_length: %d\n", rd_size); + return 0; + } + + if (rd_size == tcm_dev->max_rd_size) + return 0; + + tcm_dev->max_rd_size = MIN(rd_size, tcm_dev->max_rd_size); + + LOGD("max_rd_size = %d\n", tcm_dev->max_rd_size); + + data[0] = (unsigned char)tcm_dev->max_rd_size; + data[1] = (unsigned char)(tcm_dev->max_rd_size >> 8); + + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + CMD_TCM2_SET_MAX_READ_LENGTH, + data, + sizeof(data)); + if (retval < 0) { + LOGE("Fail to set max_read_length\n"); + return retval; + } + + return 0; +} + +/** + * syna_tcm_v2_parse_idinfo() + * + * Copy the given data to the identification info structure + * and parse the basic information, e.g. fw build id. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: data buffer + * [ in] size: size of given data buffer + * [ in] data_len: length of actual data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_parse_idinfo(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int size, unsigned int data_len) +{ + int retval; + unsigned int wr_size = 0; + unsigned int build_id = 0; + struct tcm_identification_info *id_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_len == 0)) { + LOGE("Invalid given data buffer\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + data, + size, + MIN(sizeof(*id_info), data_len)); + if (retval < 0) { + LOGE("Fail to copy identification info\n"); + return retval; + } + + build_id = syna_pal_le4_to_uint(id_info->build_id); + + wr_size = syna_pal_le2_to_uint(id_info->max_write_size); + tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE); + if (tcm_dev->max_wr_size == 0) { + tcm_dev->max_wr_size = wr_size; + LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size); + } + + LOGI("TCM Fw mode: 0x%02x\n", id_info->mode); + + if (tcm_dev->packrat_number != build_id) + tcm_dev->packrat_number = build_id; + + /* set up the max. reading length */ + retval = syna_tcm_v2_set_max_read_size(tcm_dev); + if (retval < 0) { + LOGE("Fail to setup the max reading length\n"); + return retval; + } + + tcm_dev->dev_mode = id_info->mode; + + return 0; +} + +/** + * syna_tcm_v2_dispatch_report() + * + * Handle the TouchCom report packet being received. + * + * If it's an identify report, parse the identification packet and signal + * the command completion just in case. + * Otherwise, copy the data from internal buffer.in to internal buffer.report + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v2_dispatch_report(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->report_code = tcm_msg->status_report_code; + + if (tcm_msg->payload_length == 0) { + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* The identify report may be resulted from reset or fw mode switching + */ + if (tcm_msg->report_code == REPORT_IDENTIFY) { + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v2_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* in case, the identify info packet is caused by the command */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + switch (tcm_msg->command) { + case CMD_RESET: + LOGD("Reset by CMD_RESET\n"); + case CMD_REBOOT_TO_ROM_BOOTLOADER: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE: + tcm_msg->status_report_code = STATUS_OK; + tcm_msg->response_code = STATUS_OK; + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_IDLE); + syna_pal_completion_complete(cmd_completion); + goto exit; + default: + LOGN("Device has been reset\n"); + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + goto exit; + } + } + } + + /* store the received report into the internal buffer.report */ + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->report_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.report\n"); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf, + tcm_dev->report_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to buf_report\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + tcm_dev->report_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + +exit: + return; +} + +/** + * syna_tcm_v2_dispatch_response() + * + * Handle the response packet. + * + * Copy the data from internal buffer.in to internal buffer.resp, + * and then signal the command completion. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v2_dispatch_response(struct tcm_dev *tcm_dev) +{ + int retval; + unsigned int resp_data_length; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY) + return; + + resp_data_length = tcm_msg->payload_length; + + if (resp_data_length == 0) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* store the received report into the temporary buffer */ + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf, + resp_data_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.resp\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + resp_data_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal resp_buf\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + goto exit; + } + + tcm_dev->resp_buf.data_length = resp_data_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + +exit: + syna_pal_completion_complete(cmd_completion); +} + +/** + * syna_tcm_v2_read() + * + * Read in a TouchCom packet from device. + * Checking the CRC is necessary to ensure a valid message received. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] rd_length: number of reading bytes; + * '0' means to read the message header only + * [out] buf: pointer to a buffer which is stored the retrieved data + * [out] buf_size: size of the buffer pointed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_read(struct tcm_dev *tcm_dev, unsigned int rd_length, + unsigned char **buf, unsigned int *buf_size) +{ + int retval; + struct tcm_v2_message_header *header; + int max_rd_size; + int xfer_len; + unsigned char crc6 = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + max_rd_size = tcm_dev->max_rd_size; + + /* continued packet crc if containing payload data */ + xfer_len = (rd_length > 0) ? (rd_length + 2) : rd_length; + xfer_len += sizeof(struct tcm_v2_message_header); + + if ((max_rd_size != 0) && (xfer_len > max_rd_size)) { + LOGE("Invalid xfer length, len: %d, max_rd_size: %d\n", + xfer_len, max_rd_size); + tcm_msg->status_report_code = STATUS_INVALID; + return _EINVAL; + } + + syna_tcm_buf_lock(&tcm_msg->temp); + + /* allocate the internal temp buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->temp, xfer_len); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.temp\n"); + goto exit; + } + /* read data from the bus */ + retval = syna_tcm_read(tcm_dev, + tcm_msg->temp.buf, + xfer_len); + if (retval < 0) { + LOGE("Fail to read from device\n"); + goto exit; + } + + header = (struct tcm_v2_message_header *)tcm_msg->temp.buf; + + /* check header crc always */ + crc6 = syna_tcm_v2_crc6(header->data, BITS_IN_MESSAGE_HEADER); + if (crc6 != 0) { + LOGE("Invalid header crc: 0x%02x\n", (header->byte3 & 0x3f)); + + tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED; + goto exit; + } + +#ifdef CHECK_PACKET_CRC + /* check packet crc */ + if (rd_length > 0) { + if (syna_tcm_v2_crc16(&tcm_msg->temp.buf[0], xfer_len) != 0) { + LOGE("Invalid packet crc: %02x %02x\n", + tcm_msg->temp.buf[xfer_len - 2], + tcm_msg->temp.buf[xfer_len - 1]); + + tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED; + goto exit; + } + } +#endif + + tcm_msg->status_report_code = header->code; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + + if (tcm_msg->status_report_code != STATUS_IDLE) + LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n", + tcm_msg->status_report_code, tcm_msg->payload_length, + header->data[0], header->data[1], header->data[2], + header->data[3]); + + *buf = tcm_msg->temp.buf; + *buf_size = tcm_msg->temp.buf_size; + +exit: + syna_tcm_buf_unlock(&tcm_msg->temp); + + return retval; +} + +/** + * syna_tcm_v2_write() + * + * Construct the TouchCom v2 packet and send it to device. + * Add 4-byte header at the beginning of a message and appended crc if needed. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if have any + * [ in] resend: flag for re-sending the packet + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_write(struct tcm_dev *tcm_dev, unsigned char command, + unsigned char *payload, unsigned int payload_len, bool resend) +{ + int retval; + struct tcm_v2_message_header *header; + unsigned char bits = BITS_IN_MESSAGE_HEADER - 6; + int xfer_len; + int size = MESSAGE_HEADER_SIZE + payload_len; + unsigned short crc16; + int max_wr_size; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + max_wr_size = tcm_dev->max_wr_size; + + /* continued packet crc if containing payload data */ + xfer_len = (payload_len > 0) ? (payload_len + 2) : payload_len; + xfer_len += sizeof(struct tcm_v2_message_header); + + if ((max_wr_size != 0) && (xfer_len > max_wr_size)) { + LOGE("Invalid xfer length, len: %d, max_wr_size: %d\n", + xfer_len, max_wr_size); + tcm_msg->status_report_code = STATUS_INVALID; + return _EINVAL; + } + + syna_tcm_buf_lock(&tcm_msg->out); + + /* allocate the internal out buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->out, xfer_len); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.out\n"); + goto exit; + } + + /* construct packet header */ + header = (struct tcm_v2_message_header *)tcm_msg->out.buf; + + if (resend) + tcm_msg->seq_toggle -= 1; + + header->code = command; + header->length[0] = (unsigned char)payload_len; + header->length[1] = (unsigned char)(payload_len >> 8); + header->byte3 = ((HOST_PRIMARY & 0x01) << 7); + header->byte3 |= ((tcm_msg->seq_toggle++ & 0x01) << 6); + header->byte3 |= syna_tcm_v2_crc6(header->data, bits); + + /* copy payload, if any */ + if (payload_len) { + retval = syna_pal_mem_cpy( + &tcm_msg->out.buf[MESSAGE_HEADER_SIZE], + tcm_msg->out.buf_size - MESSAGE_HEADER_SIZE, + payload, + payload_len, + payload_len); + if (retval < 0) { + LOGE("Fail to copy payload data\n"); + goto exit; + } + + /* append packet crc */ + crc16 = syna_tcm_v2_crc16(&tcm_msg->out.buf[0], size); + tcm_msg->out.buf[size] = (unsigned char)((crc16 >> 8) & 0xFF); + tcm_msg->out.buf[size + 1] = (unsigned char)(crc16 & 0xFF); + } + + /* write command packet to the bus */ + retval = syna_tcm_write(tcm_dev, + tcm_msg->out.buf, + xfer_len); + if (retval < 0) { + LOGE("Fail to write to device\n"); + goto exit; + } + +exit: + syna_tcm_buf_unlock(&tcm_msg->out); + + return retval; +} + +/** + * syna_tcm_v2_continued_read() + * + * Write a CMD_ACK to read in the remaining data payload continuously + * until the end of data. All the retrieved data is appended to the + * internal buffer.in. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: remaining data length in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_continued_read(struct tcm_dev *tcm_dev, + unsigned int length) +{ + int retval; + unsigned char *tmp_buf; + unsigned int tmp_buf_size; + int retry_cnt = 0; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + unsigned char command; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + /* continued read packet contains the header and its payload */ + total_length = MESSAGE_HEADER_SIZE + tcm_msg->payload_length; + + remaining_length = length; + + offset = tcm_msg->payload_length - length; + + syna_tcm_buf_lock(&tcm_msg->in); + + /* extend the internal buf_in if needed */ + retval = syna_tcm_buf_realloc(&tcm_msg->in, + total_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf_in\n"); + goto exit; + } + + /* available space for payload = total chunk size - header - crc */ + chunk_space = tcm_dev->max_rd_size; + if (chunk_space == 0) + chunk_space = remaining_length; + else + chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2); + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + offset += MESSAGE_HEADER_SIZE; + + /* send CMD_ACK for a continued read */ + command = CMD_TCM2_ACK; + + for (idx = 0; idx < chunks; idx++) { +retry: + LOGD("Command: 0x%02x\n", command); + + /* construct the command packet */ + retval = syna_tcm_v2_write(tcm_dev, + command, + NULL, + 0, + false); + if (retval < 0) { + LOGE("Fail to send CMD_TCM2_ACK in continued read\n"); + goto exit; + } + + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + /* read in the requested size of data */ + retval = syna_tcm_v2_read(tcm_dev, + xfer_length, + &tmp_buf, + &tmp_buf_size); + if (retval < 0) { + LOGE("Fail to read %d bytes from device\n", + xfer_length); + goto exit; + } + + /* If see an error, retry the previous read transaction + * Send RETRY instead of CMD_ACK + */ + if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED) { + if (retry_cnt > COMMAND_RETRY_TIMES) { + LOGE("Continued read packet corrupted\n"); + goto exit; + } + + retry_cnt += 1; + command = CMD_TCM2_RETRY; + + LOGW("Read corrupted, retry %d\n", retry_cnt); + goto retry; + } + + retry_cnt = 0; + command = CMD_TCM2_ACK; + + /* append data from temporary buffer to in_buf */ + syna_tcm_buf_lock(&tcm_msg->temp); + + /* copy data from internal buffer.temp to buffer.in */ + retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset], + tcm_msg->in.buf_size - offset, + &tmp_buf[MESSAGE_HEADER_SIZE], + tmp_buf_size - MESSAGE_HEADER_SIZE, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->temp); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_msg->temp); + + remaining_length -= xfer_length; + + offset += xfer_length; + } + + retval = 0; + +exit: + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v2_get_response() + * + * Read in the response packet from device. + * If containing payload data, use continued_read() function and read the + * remaining payload data. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_get_response(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_v2_message_header *header; + unsigned char *tmp_buf; + unsigned int tmp_buf_size; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + /* read in the message header at first */ + retval = syna_tcm_v2_read(tcm_dev, + 0, + &tmp_buf, + &tmp_buf_size); + if (retval < 0) { + LOGE("Fail to read message header from device\n"); + return retval; + } + + /* error out once the response packet is corrupted */ + if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED) + return 0; + + + /* allocate the required space = header + payload */ + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_buf_alloc(&tcm_msg->in, + MESSAGE_HEADER_SIZE + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to reallocate memory for internal buf.in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + retval = syna_pal_mem_cpy(tcm_msg->in.buf, + tcm_msg->in.buf_size, + tmp_buf, + tmp_buf_size, + MESSAGE_HEADER_SIZE); + if (retval < 0) { + LOGE("Fail to copy data to internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* read in payload, if any */ + if (tcm_msg->payload_length > 0) { + + retval = syna_tcm_v2_continued_read(tcm_dev, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to read in payload data, size: %d)\n", + tcm_msg->payload_length); + return retval; + } + } + + syna_tcm_buf_lock(&tcm_msg->in); + + header = (struct tcm_v2_message_header *)tcm_msg->in.buf; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + tcm_msg->status_report_code = header->code; + + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v2_send_cmd() + * + * Forward the given command and payload to syna_tcm_v2_write(). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if have any + * [ in] resend: flag for re-sending the packet + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_v2_send_cmd(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int length, bool resend) +{ + int retval; + + retval = syna_tcm_v2_write(tcm_dev, + command, + payload, + length, + resend); + if (retval < 0) + LOGE("Fail to write Command 0x%02x to device\n", command); + + return retval; +} + +/** + * syna_tcm_v2_execute_cmd_request() + * + * Process the command message. + * The helper is responsible for sending the given command and its payload, + * to device. Once the total size of message is over the wr_chunk, divide + * into continued writes + * + * In addition, the response to the command generated by the device will be + * read in immediately. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_length: length of payload in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length) +{ + int retval; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int xfer_length; + unsigned int remaining_length; + int retry_cnt = 0; + unsigned int chunk_space; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + chunk_space = tcm_dev->max_wr_size; + + remaining_length = payload_length; + + /* available space for payload = total size - header - crc */ + if (chunk_space == 0) + chunk_space = remaining_length; + else + chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2); + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = 0; + + /* process the command message and handle the response + * to the command + */ + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + +retry: + /* send command to device */ + command = (idx == 0) ? command : CMD_CONTINUE_WRITE; + + LOGD("Command: 0x%02x\n", command); + + retval = syna_tcm_v2_send_cmd(tcm_dev, + command, + &payload[offset], + xfer_length, + (retry_cnt > 0)); + if (retval < 0) + goto exit; + + /* bus turnaround delay */ + syna_pal_sleep_us(TAT_DELAY_US_MIN, TAT_DELAY_US_MAX); + + /* get the response to the command immediately */ + retval = syna_tcm_v2_get_response(tcm_dev); + if (retval < 0) { + LOGE("Fail to get the response to command 0x%02x\n", + command); + goto exit; + } + + /* check the response code */ + tcm_msg->response_code = tcm_msg->status_report_code; + + LOGD("Response code: 0x%x\n", tcm_msg->response_code); + + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + goto next; + + switch (tcm_msg->status_report_code) { + case STATUS_NO_REPORT_AVAILABLE: + case STATUS_OK: + case STATUS_ACK: + retry_cnt = 0; + break; + case STATUS_PACKET_CORRUPTED: + case STATUS_RETRY_REQUESTED: + retry_cnt += 1; + break; + default: + LOGE("Incorrect status code 0x%02x of command 0x%02x\n", + tcm_msg->status_report_code, command); + goto exit; + } + + if (retry_cnt > 0) { + if (command == CMD_RESET) { + LOGE("Command CMD_RESET corrupted, exit\n"); + /* assume ACK and wait for interrupt assertion + * once the response of reset is corrupted + */ + tcm_msg->response_code = STATUS_ACK; + goto exit; + } else if (retry_cnt > COMMAND_RETRY_TIMES) { + LOGE("Command 0x%02x corrupted\n", command); + goto exit; + } + + LOGN("Command 0x%02x, retry %d\n", command, retry_cnt); + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + + goto retry; + } +next: + offset += xfer_length; + + remaining_length -= xfer_length; + + if (chunks > 1) + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + } + +exit: + return retval; +} + +/** + * syna_tcm_v2_read_message() + * + * Send a CMD_GET_REPORT to acquire a TouchCom v2 report packet from device. + * Meanwhile, the retrieved data will be stored in the internal buffer.resp + * or buffer.report. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v2_read_message(struct tcm_dev *tcm_dev, + unsigned char *status_report_code) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (status_report_code) + *status_report_code = STATUS_INVALID; + + syna_pal_mutex_lock(rw_mutex); + + /* request a command */ + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + CMD_TCM2_GET_REPORT, + NULL, + 0); + if (retval < 0) { + LOGE("Fail to send command CMD_TCM2_GET_REPORT\n"); + + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + } + goto exit; + } + + /* duplicate the data to external buffer */ + syna_tcm_buf_lock(&tcm_dev->external_buf); + if (tcm_msg->payload_length > 0) { + retval = syna_tcm_buf_alloc(&tcm_dev->external_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory, external_buf invalid\n"); + syna_tcm_buf_unlock(&tcm_dev->external_buf); + goto exit; + } else { + retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0], + tcm_msg->payload_length, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy data to external buffer\n"); + syna_tcm_buf_unlock(&tcm_dev->external_buf); + goto exit; + } + } + } + tcm_dev->external_buf.data_length = tcm_msg->payload_length; + syna_tcm_buf_unlock(&tcm_dev->external_buf); + + if (tcm_msg->response_code == STATUS_NO_REPORT_AVAILABLE) + goto exit; + + /* process the retrieved packet */ + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + syna_tcm_v2_dispatch_report(tcm_dev); + else + syna_tcm_v2_dispatch_response(tcm_dev); + + /* copy the status report code to caller */ + if (status_report_code) + *status_report_code = tcm_msg->status_report_code; + +exit: + syna_pal_mutex_unlock(rw_mutex); + + return retval; +} + +/** + * syna_tcm_v2_write_message() + * + * Write message including command and its payload to TouchCom device. + * Then, the response of the command generated by the device will be + * read in and stored in internal buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v2_write_message(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + unsigned int delay_ms_resp) +{ + int retval; + int timeout = 0; + int polling_ms = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *cmd_mutex = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + bool has_irq_ctrl = false; + bool in_polling = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_mutex = &tcm_msg->cmd_mutex; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (resp_code) + *resp_code = STATUS_INVALID; + + /* indicate which mode is used */ + in_polling = (delay_ms_resp != RESP_IN_ATTN); + + /* irq control is enabled only when the operations is implemented + * and the current status of irq is enabled. + * do not enable irq if it is disabled by someone. + */ + has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL); + has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled; + + /* disable irq when using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false); + + LOGD("write command: 0x%02x, payload size: %d\n", + command, payload_length); + + syna_pal_mutex_lock(cmd_mutex); + + syna_pal_mutex_lock(rw_mutex); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* reset the command completion */ + syna_pal_completion_reset(cmd_completion); + + tcm_msg->command = command; + + /* request a command execution */ + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + command, + payload, + payload_length); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to device\n", command); + goto exit; + } + + syna_pal_mutex_unlock(rw_mutex); + + /* waiting for the resp data only at STATUS_ACK */ + if (tcm_msg->response_code != STATUS_ACK) { + syna_tcm_v2_dispatch_response(tcm_dev); + + goto check_response; + } + + /* handle the report generated by the command + * + * assuming to select the polling mode, the while-loop below will + * repeatedly read in the respose data based on the given polling + * time; otherwise, wait until receiving a completion event from + * interupt thread. + */ + timeout = 0; + if (!in_polling) + polling_ms = CMD_RESPONSE_TIMEOUT_MS; + else + polling_ms = delay_ms_resp; + + do { + /* wait for the completion event triggered by read_message */ + retval = syna_pal_completion_wait_for(cmd_completion, + polling_ms); + /* reset the status when times out and keep in polling */ + if (retval < 0) + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* break when geting a valid resp; otherwise, keep in polling */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE) + goto check_response; + + if (in_polling) { + /* retrieve the message packet back */ + retval = syna_tcm_v2_read_message(tcm_dev, NULL); + /* keep in polling if still not having a valid resp */ + if (retval < 0) + syna_pal_completion_reset(cmd_completion); + } + + timeout += polling_ms + 10; + + } while (timeout < CMD_RESPONSE_TIMEOUT_MS); + + /* check the status of response data + * according to the touchcomm spec, each command message + * should have an associated reponse message. + */ +check_response: + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) { + if (timeout >= CMD_RESPONSE_TIMEOUT_MS) { + LOGE("Timed out wait for response of command 0x%02x\n", + command); + retval = _ETIMEDOUT; + goto exit; + } else { + LOGE("Fail to get valid response of command 0x%02x\n", + command); + retval = _EIO; + goto exit; + } + } + + /* copy response code to the caller */ + if (resp_code) + *resp_code = tcm_msg->response_code; + + if (tcm_msg->response_code != STATUS_OK) { + LOGE("Error code 0x%02x of command 0x%02x\n", + tcm_msg->response_code, tcm_msg->command); + retval = _EIO; + } else { + retval = 0; + } + +exit: + tcm_msg->command = CMD_NONE; + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + syna_pal_mutex_unlock(cmd_mutex); + + /* recovery the irq if using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true); + + return retval; +} + + /** + * syna_tcm_v2_detect() + * + * For TouchCom v2 protocol, the given data must have a valid crc-6 at the end. + * If so, send an identify command to identify the device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int size) +{ + int retval; + unsigned char resp_code = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (size < MESSAGE_HEADER_SIZE)) { + LOGE("Invalid parameters\n"); + return _EINVAL; + } + + if (syna_tcm_v2_crc6(data, BITS_IN_MESSAGE_HEADER) != 0) + return _ENODEV; + + /* send an identify command to identify the device */ + retval = syna_tcm_v2_write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + RESP_IN_POLLING); + if (retval < 0) { + LOGE("Fail to get identification info from device\n"); + return retval; + } + + /* expose the read / write operations */ + tcm_dev->read_message = syna_tcm_v2_read_message; + tcm_dev->write_message = syna_tcm_v2_write_message; + + return retval; +} diff --git a/tcm/synaptics_touchcom_func_base.c b/tcm/synaptics_touchcom_func_base.c new file mode 100644 index 0000000..8e83c15 --- /dev/null +++ b/tcm/synaptics_touchcom_func_base.c @@ -0,0 +1,1678 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_base.c + * + * This file implements generic and foundational functions supported in + * Synaptics TouchComm communication protocol. + * + * The declarations are in synaptics_touchcom_func_base.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_touch.h" + + +/** + * syna_tcm_change_resp_read() + * + * Helper to change the default resp reading method, which was previously set + * when calling syna_tcm_allocate_device + * + * @param + * [in] tcm_dev: the device handle + * [in] request: resp reading method to change + * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise, + * assign a positive value standing for the polling time + * @return + * none. + */ +void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request) +{ + if (request == RESP_IN_ATTN) { + tcm_dev->msg_data.default_resp_reading = RESP_IN_ATTN; + + LOGI("Change default resp reading method by attn\n"); + } else { + if (request < RESP_IN_POLLING) + request = RESP_IN_POLLING; + + tcm_dev->msg_data.default_resp_reading = request; + + LOGI("Change default resp reading method by polling (%dms)\n", + tcm_dev->msg_data.default_resp_reading); + } +} + +/** + * syna_tcm_init_message_wrap() + * + * Initialize internal buffers and related structures for command processing. + * The function must be called to prepare all essential structures for + * command wrapper. + * + * @param + * [in] tcm_msg: message wrapper structure + * [in] resp_reading: default method to retrieve the resp data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_init_message_wrap(struct tcm_message_data_blob *tcm_msg, + unsigned int resp_reading) +{ + /* initialize internal buffers */ + syna_tcm_buf_init(&tcm_msg->in); + syna_tcm_buf_init(&tcm_msg->out); + syna_tcm_buf_init(&tcm_msg->temp); + + /* allocate the completion event for command processing */ + if (syna_pal_completion_alloc(&tcm_msg->cmd_completion) < 0) { + LOGE("Fail to allocate cmd completion event\n"); + return _EINVAL; + } + + /* allocate the cmd_mutex for command protection */ + if (syna_pal_mutex_alloc(&tcm_msg->cmd_mutex) < 0) { + LOGE("Fail to allocate cmd_mutex\n"); + return _EINVAL; + } + + /* allocate the rw_mutex for rw protection */ + if (syna_pal_mutex_alloc(&tcm_msg->rw_mutex) < 0) { + LOGE("Fail to allocate rw_mutex\n"); + return _EINVAL; + } + + /* set default state of command_status */ + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + /* allocate the internal buffer.in at first */ + syna_tcm_buf_lock(&tcm_msg->in); + + if (syna_tcm_buf_alloc(&tcm_msg->in, MESSAGE_HEADER_SIZE) < 0) { + LOGE("Fail to allocate memory for buf.in (size = %d)\n", + MESSAGE_HEADER_SIZE); + tcm_msg->in.buf_size = 0; + tcm_msg->in.data_length = 0; + syna_tcm_buf_unlock(&tcm_msg->in); + return _EINVAL; + } + tcm_msg->in.buf_size = MESSAGE_HEADER_SIZE; + + syna_tcm_buf_unlock(&tcm_msg->in); + + tcm_msg->default_resp_reading = resp_reading; + + LOGI("Resp reading method (default): %s\n", + (resp_reading == RESP_IN_ATTN) ? "attn" : "polling"); + + return 0; +} + +/** + * syna_tcm_del_message_wrap() + * + * Remove message wrapper interface and internal buffers. + * Call the function once the message wrapper is no longer needed. + * + * @param + * [in] tcm_msg: message wrapper structure + * + * @return + * none. + */ +static void syna_tcm_del_message_wrap(struct tcm_message_data_blob *tcm_msg) +{ + /* release the mutex */ + syna_pal_mutex_free(&tcm_msg->rw_mutex); + syna_pal_mutex_free(&tcm_msg->cmd_mutex); + + /* release the completion event */ + syna_pal_completion_free(&tcm_msg->cmd_completion); + + /* release internal buffers */ + syna_tcm_buf_release(&tcm_msg->temp); + syna_tcm_buf_release(&tcm_msg->out); + syna_tcm_buf_release(&tcm_msg->in); +} + +/** + * syna_tcm_allocate_device() + * + * Create the TouchCom core device handle. + * This function must be called in order to allocate the main device handle, + * structure syna_tcm_dev, which will be passed to all other operations and + * functions within the entire source code. + * + * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure, + * so that all the implemented functions can access hardware components + * through syna_tcm_hw_interface. + * + * @param + * [out] ptcm_dev_ptr: a pointer to the device handle returned + * [ in] hw_if: hardware-specific data on target platform + * [ in] resp_reading: default resp reading method + * set 'RESP_IN_ATTN' to apply ATTN-driven method; + * set 'RESP_IN_POLLING' to read in resp by polling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr, + struct syna_hw_interface *hw_if, unsigned int resp_reading) +{ + int retval = 0; + struct tcm_dev *tcm_dev = NULL; + + if (!hw_if) { + LOGE("Invalid parameter of hw_if\n"); + return _EINVAL; + } + + if ((!hw_if->ops_read_data) || (!hw_if->ops_write_data)) { + LOGE("Invalid hw read write operation\n"); + return _EINVAL; + } + + *ptcm_dev_ptr = NULL; + + /* allocate the core device handle */ + tcm_dev = (struct tcm_dev *)syna_pal_mem_alloc( + 1, + sizeof(struct tcm_dev)); + if (!tcm_dev) { + LOGE("Fail to create tcm device handle\n"); + return _ENOMEM; + } + + /* link to the given hardware data */ + tcm_dev->hw_if = hw_if; + + tcm_dev->max_rd_size = hw_if->bdata_io.rd_chunk_size; + tcm_dev->max_wr_size = hw_if->bdata_io.wr_chunk_size; + + tcm_dev->write_message = NULL; + tcm_dev->read_message = NULL; + + /* allocate internal buffers */ + syna_tcm_buf_init(&tcm_dev->report_buf); + syna_tcm_buf_init(&tcm_dev->resp_buf); + syna_tcm_buf_init(&tcm_dev->external_buf); + syna_tcm_buf_init(&tcm_dev->touch_config); + + /* initialize the command wrapper interface */ + retval = syna_tcm_init_message_wrap(&tcm_dev->msg_data, + resp_reading); + if (retval < 0) { + LOGE("Fail to initialize command interface\n"); + goto err_init_message_wrap; + } + + /* return the created device handle */ + *ptcm_dev_ptr = tcm_dev; + + LOGI("TouchComm core module created, ver.: %d.%02d\n", + (unsigned char)(SYNA_TCM_CORE_LIB_VERSION >> 8), + (unsigned char)SYNA_TCM_CORE_LIB_VERSION & 0xff); + + LOGI("Capability: wr_chunk(%d), rd_chunk(%d), irq_control(%s)\n", + tcm_dev->max_wr_size, tcm_dev->max_rd_size, + (hw_if->ops_enable_irq) ? "yes" : "no"); + + return 0; + +err_init_message_wrap: + syna_tcm_buf_release(&tcm_dev->touch_config); + syna_tcm_buf_release(&tcm_dev->external_buf); + syna_tcm_buf_release(&tcm_dev->report_buf); + syna_tcm_buf_release(&tcm_dev->resp_buf); + + tcm_dev->hw_if = NULL; + + syna_pal_mem_free((void *)tcm_dev); + + return retval; +} + +/** + * syna_tcm_remove_device() + * + * Remove the TouchCom core device handler. + * This function must be invoked when the device is no longer needed. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +void syna_tcm_remove_device(struct tcm_dev *tcm_dev) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + /* release the command interface */ + syna_tcm_del_message_wrap(&tcm_dev->msg_data); + + /* release buffers */ + syna_tcm_buf_release(&tcm_dev->touch_config); + syna_tcm_buf_release(&tcm_dev->external_buf); + syna_tcm_buf_release(&tcm_dev->report_buf); + syna_tcm_buf_release(&tcm_dev->resp_buf); + + tcm_dev->hw_if = NULL; + + /* release the device handle */ + syna_pal_mem_free((void *)tcm_dev); + + LOGI("tcm device handle removed\n"); +} + +/** + * syna_tcm_detect_protocol() + * + * Helper to distinguish which TouchCom firmware is running. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw data from device + * [ in] data_len: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_detect_protocol(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int data_len) +{ + int retval; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_v2_detect(tcm_dev, data, data_len); + if (retval < 0) + retval = syna_tcm_v1_detect(tcm_dev, data, data_len); + + return retval; +} + +/** + * syna_tcm_detect_device() + * + * Determine the type of device being connected, and distinguish which + * version of TouchCom firmware running on the device. + * This function must be called before using this TouchComm core library. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, the current mode running on the device is returned; + * otherwise, negative value on error. + */ +int syna_tcm_detect_device(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char data[4] = { 0 }; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->dev_mode = MODE_UNKNOWN; + + /* get the bare data from the bus directly */ + data[0] = 0x07; + retval = syna_tcm_write(tcm_dev, &data[0], 1); + if (retval < 0) { + LOGE("Fail to write magic to bus\n"); + return _EIO; + } + + retval = syna_tcm_read(tcm_dev, + data, (unsigned int)sizeof(data)); + if (retval < 0) { + LOGE("Fail to retrieve 4-byte data from bus\n"); + return _EIO; + } + + LOGD("bare data: %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + + /* distinguish which tcm version running on the device */ + retval = syna_tcm_detect_protocol(tcm_dev, + data, (unsigned int)sizeof(data)); + if (retval < 0) { + LOGE("Fail to detect TouchCom device, %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + return retval; + } + + if ((!tcm_dev->write_message) || (!tcm_dev->read_message)) { + LOGE("Invalid TouchCom rw operations\n"); + return _ENODEV; + } + + /* check the running mode */ + switch (tcm_dev->dev_mode) { + case MODE_APPLICATION_FIRMWARE: + LOGI("Device in Application FW, build id: %d, %s\n", + tcm_dev->packrat_number, + tcm_dev->id_info.part_number); + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + LOGI("Device in Bootloader\n"); + break; + case MODE_ROMBOOTLOADER: + LOGI("Device in ROMBoot uBL\n"); + break; + case MODE_MULTICHIP_TDDI_BOOTLOADER: + LOGI("Device in multi-chip TDDI Bootloader\n"); + break; + default: + LOGW("Found TouchCom device, but unsupported mode: 0x%02x\n", + tcm_dev->dev_mode); + break; + } + + retval = tcm_dev->dev_mode; + return retval; +} + +/** + * syna_tcm_get_event_data() + * + * Helper to read TouchComm messages when ATTN signal is asserted. + * After returning, the ATTN signal should be no longer asserted. + * + * The 'code' returned will guide the caller on the next action. + * For example, do touch reporting once returned code is equal to REPORT_TOUCH. + * + * @param + * [ in] tcm_dev: the device handle + * [out] code: received report code + * [out] data: a user buffer for data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_event_data(struct tcm_dev *tcm_dev, + unsigned char *code, struct tcm_buffer *data) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!code) { + LOGE("Invalid parameter\n"); + return _EINVAL; + } + + /* retrieve the event data */ + retval = tcm_dev->read_message(tcm_dev, + code); + if (retval < 0) { + LOGE("Fail to read messages\n"); + return retval; + } + + /* exit if no buffer provided */ + if (!data) + goto exit; + + /* if gathering a report, copy to the user buffer */ + if ((*code >= REPORT_IDENTIFY) && (*code != STATUS_INVALID)) { + if (tcm_dev->report_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_copy(data, &tcm_dev->report_buf); + if (retval < 0) { + LOGE("Fail to copy data, report type: %x\n", *code); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->report_buf); + } + + /* if gathering a response, copy to the user buffer */ + if ((*code > STATUS_IDLE) && (*code <= STATUS_ERROR)) { + if (tcm_dev->resp_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_copy(data, &tcm_dev->resp_buf); + if (retval < 0) { + LOGE("Fail to copy data, status code: %x\n", *code); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + } + +exit: + return retval; +} + +/** + * syna_tcm_identify() + * + * Implement the standard command code, which is used to request + * an IDENTIFY report packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] id_info: the identification info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_identify(struct tcm_dev *tcm_dev, + struct tcm_identification_info *id_info) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", CMD_IDENTIFY); + goto exit; + } + + tcm_dev->dev_mode = tcm_dev->id_info.mode; + + if (id_info == NULL) + goto show_info; + + /* copy identify info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + MIN(sizeof(*id_info), tcm_dev->resp_buf.data_length)); + if (retval < 0) { + LOGE("Fail to copy identify info to caller\n"); + goto exit; + } + +show_info: + LOGI("TCM Fw mode: 0x%02x, TCM ver.: %d\n", + tcm_dev->id_info.mode, tcm_dev->id_info.version); + +exit: + return retval; +} + +/** + * syna_tcm_reset() + * + * Implement the standard command code, which is used to perform a sw reset + * immediately. After a successful reset, an IDENTIFY report to indicate that + * device is ready. + * + * Caller shall be aware that the firmware will be reloaded after reset. + * Therefore, if expecting that a different firmware version is loaded, please + * do app firmware setup after reset. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_reset(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + unsigned int board_setting; + unsigned int resp_handling; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + resp_handling = tcm_dev->msg_data.default_resp_reading; + + /* gather the board settings of reset delay time */ + board_setting = tcm_dev->hw_if->bdata_rst.reset_delay_ms; + if (board_setting == 0) + board_setting = RESET_DELAY_MS; + + /* select the proper period to handle the resp of reset */ + if (resp_handling != RESP_IN_ATTN) { + if (board_setting > resp_handling) { + resp_handling = board_setting; + LOGI("Use board settings %dms to poll resp of reset\n", + resp_handling); + } + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RESET, + NULL, + 0, + &resp_code, + resp_handling); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", CMD_RESET); + goto exit; + } + + /* current device mode is expected to be updated + * because identification report will be received after reset + */ + tcm_dev->dev_mode = tcm_dev->id_info.mode; + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGI("Device mode 0x%02X running after reset\n", + tcm_dev->dev_mode); + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_enable_report() + * + * Implement the application fw command code to enable or disable a report. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report_code: the requested report code being generated + * [ in] en: '1' for enabling; '0' for disabling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_report(struct tcm_dev *tcm_dev, + unsigned char report_code, bool en) +{ + int retval = 0; + unsigned char resp_code; + unsigned char command; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + command = (en) ? CMD_ENABLE_REPORT : CMD_DISABLE_REPORT; + + retval = tcm_dev->write_message(tcm_dev, + command, + &report_code, + 1, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to %s 0x%02x report\n", + command, (en)?"enable":"disable", report_code); + goto exit; + } + + if (resp_code != STATUS_OK) { + LOGE("Fail to %s 0x%02x report, resp_code:%x\n", + (en) ? "enable" : "disable", report_code, resp_code); + } else { + LOGD("Report 0x%x %s\n", report_code, + (en) ? "enabled" : "disabled"); + } + +exit: + return retval; +} + +/** + * syna_tcm_run_rom_bootloader_fw() + * + * Requests that the rombootloader firmware be run. + * Once the rombootloader firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_rom_bootloader_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_REBOOT_TO_ROM_BOOTLOADER, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_REBOOT_TO_ROM_BOOTLOADER); + goto exit; + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter rom bootloader, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("ROM Bootloader (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_run_bootloader_fw() + * + * Requests that the bootloader firmware be run. + * Once the bootloader firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_bootloader_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RUN_BOOTLOADER_FIRMWARE, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_RUN_BOOTLOADER_FIRMWARE); + goto exit; + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter bootloader, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("Bootloader Firmware (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_run_application_fw() + * + * Requests that the application firmware be run. + * Once the application firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_application_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_RUN_APPLICATION_FIRMWARE); + goto exit; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGW("Fail to enter application fw, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("Application Firmware (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_switch_fw_mode() + * + * Implement the command code to switch the firmware mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] mode: target firmware mode + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev, + unsigned char mode, unsigned int fw_switch_delay) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + switch (mode) { + case MODE_APPLICATION_FIRMWARE: + retval = syna_tcm_run_application_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to application mode\n"); + goto exit; + } + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + case MODE_TDDI_HDL_BOOTLOADER: + case MODE_MULTICHIP_TDDI_BOOTLOADER: + retval = syna_tcm_run_bootloader_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to bootloader mode\n"); + goto exit; + } + break; + case MODE_ROMBOOTLOADER: + retval = syna_tcm_run_rom_bootloader_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to rom bootloader mode\n"); + goto exit; + } + break; + default: + LOGE("Invalid firmware mode requested\n"); + retval = _EINVAL; + goto exit; + } + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_get_boot_info() + * + * Implement the bootloader command code, which is used to request a + * boot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int resp_data_len = 0; + unsigned int copy_size; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_BOOT_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_BOOT_INFO); + goto exit; + } + + resp_data_len = tcm_dev->resp_buf.data_length; + copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len); + + /* save the boot_info */ + retval = syna_pal_mem_cpy((unsigned char *)&tcm_dev->boot_info, + sizeof(struct tcm_boot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy boot info\n"); + goto exit; + } + + if (boot_info == NULL) + goto exit; + + /* copy boot_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)boot_info, + sizeof(struct tcm_boot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy boot info to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_get_app_info() + * + * Implement the application fw command code to request an application + * info packet from device. + * + * @param + * [ in] tcm_dev: the device handle + * [out] app_info: the application info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_app_info(struct tcm_dev *tcm_dev, + struct tcm_application_info *app_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int app_status; + unsigned int resp_data_len = 0; + unsigned int copy_size; + struct tcm_application_info *info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_APPLICATION_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_APPLICATION_INFO); + goto exit; + } + + resp_data_len = tcm_dev->resp_buf.data_length; + copy_size = MIN(sizeof(tcm_dev->app_info), resp_data_len); + + info = &tcm_dev->app_info; + + /* save the app_info */ + retval = syna_pal_mem_cpy((unsigned char *)info, + sizeof(struct tcm_application_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy application info\n"); + goto exit; + } + + if (app_info == NULL) + goto show_info; + + /* copy app_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)app_info, + sizeof(struct tcm_application_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy application info to caller\n"); + goto exit; + } + +show_info: + app_status = syna_pal_le2_to_uint(tcm_dev->app_info.status); + + if (app_status == APP_STATUS_BAD_APP_CONFIG) { + LOGE("Bad application firmware, status: 0x%x\n", app_status); + retval = _ENODEV; + goto exit; + } else if (app_status != APP_STATUS_OK) { + LOGE("Incorrect application status, 0x%x\n", app_status); + retval = _ENODEV; + goto exit; + } + + tcm_dev->max_objects = syna_pal_le2_to_uint(info->max_objects); + tcm_dev->max_x = syna_pal_le2_to_uint(info->max_x); + tcm_dev->max_y = syna_pal_le2_to_uint(info->max_y); + + tcm_dev->cols = syna_pal_le2_to_uint(info->num_of_image_cols); + tcm_dev->rows = syna_pal_le2_to_uint(info->num_of_image_rows); + syna_pal_mem_cpy((unsigned char *)tcm_dev->config_id, + MAX_SIZE_CONFIG_ID, + info->customer_config_id, + MAX_SIZE_CONFIG_ID, + MAX_SIZE_CONFIG_ID); + + LOGD("App info version: %d, status: %d\n", + syna_pal_le2_to_uint(info->version), app_status); + LOGD("App info: max_objs: %d, max_x:%d, max_y: %d, img: %dx%d\n", + tcm_dev->max_objects, tcm_dev->max_x, tcm_dev->max_y, + tcm_dev->rows, tcm_dev->cols); + +exit: + return retval; +} + +/** + * syna_tcm_get_static_config() + * + * Implement the application fw command code to retrieve the contents of + * the static configuration. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] buf: buffer stored the static configuration + * [ in] buf_size: the size of given buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_static_config(struct tcm_dev *tcm_dev, + unsigned char *buf, unsigned int buf_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + size = syna_pal_le2_to_uint(app_info->static_config_size); + + if (size > buf_size) { + LOGE("Invalid buffer input, given size: %d (actual: %d)\n", + buf_size, size); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_STATIC_CONFIG, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_STATIC_CONFIG); + goto exit; + } + + if (buf == NULL) + goto exit; + + /* copy app_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)buf, + buf_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + tcm_dev->resp_buf.data_length); + if (retval < 0) { + LOGE("Fail to copy static config data to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_set_static_config() + * + * Implement the application fw command code to set the contents of + * the static configuration. When the write is completed, the device will + * restart touch sensing with the new settings. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_data: the data of static configuration + * [ in] config_data_size: the size of given data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_static_config(struct tcm_dev *tcm_dev, + unsigned char *config_data, unsigned int config_data_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + size = syna_pal_le2_to_uint(app_info->static_config_size); + + if (size != config_data_size) { + LOGE("Invalid static config size, given: %d (actual: %d)\n", + config_data_size, size); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_STATIC_CONFIG, + config_data, + config_data_size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_SET_STATIC_CONFIG); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_get_dynamic_config() + * + * Implement the application fw command code to get the value from the a single + * field of the dynamic configuration. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [out] value: the value returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short *value, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char out; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + out = (unsigned char)id; + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_DYNAMIC_CONFIG, + &out, + sizeof(out), + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to get dynamic field 0x%x\n", + CMD_GET_DYNAMIC_CONFIG, (unsigned char)id); + goto exit; + } + + /* return dynamic config data */ + if (tcm_dev->resp_buf.data_length < 2) { + LOGE("Invalid resp data size, %d\n", + tcm_dev->resp_buf.data_length); + goto exit; + } + + *value = (unsigned short)syna_pal_le2_to_uint(tcm_dev->resp_buf.buf); + + LOGD("Get %d from dynamic field 0x%x\n", *value, id); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_set_dynamic_config() + * + * Implement the application fw command code to set the specified value to + * the selected field of the dynamic configuration. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [ in] value: the value to the selected field + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short value, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char out[3]; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + LOGD("Set %d to dynamic field 0x%x\n", value, id); + + out[0] = (unsigned char)id; + out[1] = (unsigned char)value; + out[2] = (unsigned char)(value >> 8); + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_DYNAMIC_CONFIG, + out, + sizeof(out), + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to set %d to field 0x%x\n", + CMD_SET_DYNAMIC_CONFIG, value, (unsigned char)id); + goto exit; + } + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_rezero() + * + * Implement the application fw command code to force the device to rezero its + * baseline estimate. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_rezero(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_REZERO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_REZERO); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_set_config_id() + * + * Implement the application fw command code to set the 16-byte config id, + * which can be read in the app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_id: config id to be set + * [ in] size: size of input data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_config_id(struct tcm_dev *tcm_dev, + unsigned char *config_id, unsigned int size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int config_id_len = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + config_id_len = sizeof(tcm_dev->app_info.customer_config_id); + + if (size != config_id_len) { + LOGE("Invalid config id input, given size: %d (%d)\n", + size, config_id_len); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_CONFIG_ID, + config_id, + size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_SET_CONFIG_ID); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_sleep() + * + * Implement the application fw command code to put the device into low power + * deep sleep mode or set to normal active mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en) +{ + int retval = 0; + unsigned char resp_code; + unsigned char command; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + command = (en) ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP; + + retval = tcm_dev->write_message(tcm_dev, + command, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%x\n", command); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_get_features() + * + * Implement the application fw command code to query the supported features. + * + * @param + * [ in] tcm_dev: the device handle + * [out] info: the features description packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_features(struct tcm_dev *tcm_dev, + struct tcm_features_info *info) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_FEATURES, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_FEATURES); + goto exit; + } + + if (info == NULL) + goto exit; + + /* copy features_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)info, + sizeof(struct tcm_features_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + MIN(sizeof(*info), tcm_dev->resp_buf.data_length)); + if (retval < 0) { + LOGE("Fail to copy features_info to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_run_production_test() + * + * Implement the appplication fw command code to request the device to run + * the production test. + * + * Production tests are listed at enum test_code (PID$). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] test_item: the requested testing item + * [out] tdata: testing data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_run_production_test(struct tcm_dev *tcm_dev, + unsigned char test_item, struct tcm_buffer *tdata) +{ + int retval = 0; + unsigned char resp_code; + unsigned char test_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + test_code = (unsigned char)test_item; + + retval = tcm_dev->write_message(tcm_dev, + CMD_PRODUCTION_TEST, + &test_code, + 1, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_PRODUCTION_TEST); + goto exit; + } + + if (tdata == NULL) + goto exit; + + /* copy testing data to caller */ + retval = syna_tcm_buf_copy(tdata, &tcm_dev->resp_buf); + if (retval < 0) { + LOGE("Fail to copy testing data\n"); + goto exit; + } +exit: + return retval; +} +/** + * syna_tcm_send_command() + * + * Helper to forward the custom commnd to the device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [out] resp: buffer to store the response data + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + struct tcm_buffer *resp, unsigned int delay_ms_resp) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!resp_code) { + LOGE("Invalid parameter\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + command, + payload, + payload_length, + resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + command); + goto exit; + } + + /* response data returned */ + if ((resp != NULL) && (tcm_dev->resp_buf.data_length > 0)) { + retval = syna_tcm_buf_copy(resp, &tcm_dev->resp_buf); + if (retval < 0) { + LOGE("Fail to copy resp data\n"); + goto exit; + } + } + +exit: + return retval; +} + diff --git a/tcm/synaptics_touchcom_func_base.h b/tcm/synaptics_touchcom_func_base.h new file mode 100644 index 0000000..d0a155b --- /dev/null +++ b/tcm/synaptics_touchcom_func_base.h @@ -0,0 +1,418 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_base.h + * + * This file declares generic and foundational APIs being used to communicate + * with Synaptics touch controller through TouchComm communication protocol. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * syna_tcm_allocate_device() + * + * Create the TouchCom core device handle. + * This function must be called in order to allocate the main device handle, + * structure syna_tcm_dev, which will be passed to all other operations and + * functions within the entire source code. + * + * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure, + * so that all the implemented functions can access hardware components + * through syna_tcm_hw_interface. + * + * @param + * [out] ptcm_dev_ptr: a pointer to the device handle returned + * [ in] hw_if: hardware-specific data on target platform + * [ in] resp_reading: default resp reading method + * set 'RESP_IN_ATTN' to apply ATTN-driven method; + * set 'RESP_IN_POLLING' to read in resp by polling + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr, + struct syna_hw_interface *hw_if, unsigned int resp_reading); + +/** + * syna_tcm_remove_device() + * + * Remove the TouchCom core device handle. + * This function must be invoked when the device is no longer needed. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +void syna_tcm_remove_device(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_detect_device() + * + * Determine the type of device being connected and distinguish which + * version of TouchCom firmware running on the device. + * This function should be called before using this TouchComm core library. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, the current mode running on the device is returned; + * otherwise, negative value on error. + */ +int syna_tcm_detect_device(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_get_event_data() + * + * Helper to read TouchComm messages when ATTN signal is asserted. + * After returning, the ATTN signal should be no longer asserted. + * + * The 'code' returned will guide the caller on the next action. + * For example, do touch reporting once returned code is equal to REPORT_TOUCH. + * + * @param + * [ in] tcm_dev: the device handle + * [out] code: received report code + * [out] report: report data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_event_data(struct tcm_dev *tcm_dev, + unsigned char *code, + struct tcm_buffer *report); + +/** + * syna_tcm_change_resp_read() + * + * Helper to change the default resp reading method, which was previously set + * when calling syna_tcm_allocate_device + * + * @param + * [in] tcm_dev: the device handle + * [in] request: resp reading method to change + * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise, + * assign a positive value standing for the polling time + * @return + * none. + */ +void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request); + +/** + * syna_tcm_identify() + * + * Implement the standard command code, which is used to request + * an IDENTIFY report packet + * + * @param + * [ in] tcm_dev: the device handle + * [out] id_info: the identification info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_identify(struct tcm_dev *tcm_dev, + struct tcm_identification_info *id_info); + +/** + * syna_tcm_reset() + * + * Implement the standard command code, which is used to perform a sw reset + * immediately. After a successful reset, an IDENTIFY report to indicate that + * device is ready. + * + * Caller shall be aware that the firmware will be reloaded after reset. + * Therefore, if expecting that a different firmware version is loaded, please + * do app firmware setup after reset. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_reset(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_enable_report() + * + * Implement the application fw command code to enable or disable a report. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report_code: the requested report code being generated + * [ in] en: '1' for enabling; '0' for disabling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_report(struct tcm_dev *tcm_dev, + unsigned char report_code, bool en); + +/** + * syna_tcm_switch_fw_mode() + * + * Implement the command code to switch the firmware mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] mode: target firmware mode + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev, + unsigned char mode, unsigned int fw_switch_delay); + +/** + * syna_tcm_get_boot_info() + * + * Implement the bootloader command code, which is used to request a + * boot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info); + +/** + * syna_tcm_get_app_info() + * + * Implement the application fw command code to request an application + * info packet from device + * + * @param + * [ in] tcm_dev: the device handle + * [out] app_info: the application info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_app_info(struct tcm_dev *tcm_dev, + struct tcm_application_info *app_info); + +/** + * syna_tcm_get_static_config() + * + * Implement the application fw command code to retrieve the contents of + * the static configuration. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] buf: buffer stored the static configuration + * [ in] buf_size: the size of given buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_static_config(struct tcm_dev *tcm_dev, + unsigned char *buf, unsigned int buf_size); + +/** + * syna_tcm_set_static_config() + * + * Implement the application fw command code to set the contents of + * the static configuration. When the write is completed, the device will + * restart touch sensing with the new settings + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_data: the data of static configuration + * [ in] config_data_size: the size of given data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_static_config(struct tcm_dev *tcm_dev, + unsigned char *config_data, unsigned int config_data_size); + +/** + * syna_tcm_get_dynamic_config() + * + * Implement the application fw command code to get the value from the a single + * field of the dynamic configuration + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [out] value: the value returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short *value, + unsigned int delay_ms_resp); + +/** + * syna_tcm_set_dynamic_config() + * + * Implement the application fw command code to set the specified value to + * the selected field of the dynamic configuration + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [ in] value: the value to the selected field + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short value, + unsigned int delay_ms_resp); + +/** + * syna_tcm_rezero() + * + * Implement the application fw command code to force the device to rezero its + * baseline estimate. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_rezero(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_set_config_id() + * + * Implement the application fw command code to set the 16-byte config id, + * which can be read in the app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_id: config id to be set + * [ in] size: size of input data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_config_id(struct tcm_dev *tcm_dev, + unsigned char *config_id, unsigned int size); + +/** + * syna_tcm_sleep() + * + * Implement the application fw command code to put the device into low power + * deep sleep mode or set to normal active mode + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en); + +/** + * syna_tcm_get_features() + * + * Implement the application fw command code to query the supported features. + * + * @param + * [ in] tcm_dev: the device handle + * [out] info: the features description packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_features(struct tcm_dev *tcm_dev, + struct tcm_features_info *info); + +/** + * syna_tcm_run_production_test() + * + * Implement the appplication fw command code to request the device to run + * the production test. + * + * Production tests are listed at enum test_code (PID$). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] test_item: the requested testing item + * [out] tdata: testing data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_run_production_test(struct tcm_dev *tcm_dev, + unsigned char test_item, struct tcm_buffer *tdata); + +/** + * syna_tcm_send_command() + * + * Helper to forward the custom commnd to the device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [out] resp: buffer to store the response data + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + struct tcm_buffer *resp, unsigned int delay_ms_resp); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ */ diff --git a/tcm/synaptics_touchcom_func_base_flash.h b/tcm/synaptics_touchcom_func_base_flash.h new file mode 100644 index 0000000..87d0a4e --- /dev/null +++ b/tcm/synaptics_touchcom_func_base_flash.h @@ -0,0 +1,569 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_base_flash.h + * + * This file declares the common functions and structures being used in relevant + * functions of fw update. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ +#define _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * @section: Some specific definition in the firmware file + */ +#define ID_STRING_SIZE (32) + +#define SIZE_WORDS (8) + +#define IHEX_RECORD_SIZE (14) + +#define IHEX_MAX_BLOCKS (64) + +#define IMAGE_FILE_MAGIC_VALUE (0x4818472b) + +#define FLASH_AREA_MAGIC_VALUE (0x7c05e516) + + +/** + * @section: Helper macros for firmware file parsing + */ +#define CRC32(data, length) \ + (syna_pal_crc32(~0, data, length) ^ ~0) + +#define VALUE(value) \ + (syna_pal_le2_to_uint(value)) + +#define AREA_ID_STR(area) \ + (syna_tcm_get_flash_area_string(area)) + + +/** + * @section: Area Partitions in firmware + */ +enum flash_area { + AREA_NONE = 0, + /* please add the declarations below */ + + AREA_BOOT_CODE, + AREA_BOOT_CONFIG, + AREA_APP_CODE, + AREA_APP_CODE_COPRO, + AREA_APP_CONFIG, + AREA_PROD_TEST, + AREA_DISP_CONFIG, + AREA_F35_APP_CODE, + AREA_FORCE_TUNING, + AREA_GAMMA_TUNING, + AREA_TEMPERATURE_GAMM_TUNING, + AREA_CUSTOM_LCM, + AREA_LOOKUP, + AREA_CUSTOM_OEM, + AREA_OPEN_SHORT_TUNING, + AREA_CUSTOM_OTP, + AREA_PPDT, + AREA_ROMBOOT_APP_CODE, + AREA_TOOL_BOOT_CONFIG, + + /* please add the declarations above */ + AREA_MAX, +}; +/** + * @section: String of Area Partitions in firmware + */ +static char *flash_area_str[] = { + NULL, + /* please add the declarations below */ + + "BOOT_CODE", /* AREA_BOOT_CODE */ + "BOOT_CONFIG", /* AREA_BOOT_CONFIG */ + "APP_CODE", /* AREA_APP_CODE */ + "APP_CODE_COPRO", /* AREA_APP_CODE_COPRO */ + "APP_CONFIG", /* AREA_APP_CONFIG */ + "APP_PROD_TEST", /* AREA_PROD_TEST */ + "DISPLAY", /* AREA_DISP_CONFIG */ + "F35_APP_CODE", /* AREA_F35_APP_CODE */ + "FORCE", /* AREA_FORCE_TUNING */ + "GAMMA", /* AREA_GAMMA_TUNING */ + "TEMPERATURE_GAMM",/* AREA_TEMPERATURE_GAMM_TUNING */ + "LCM", /* AREA_CUSTOM_LCM */ + "LOOKUP", /* AREA_LOOKUP */ + "OEM", /* AREA_CUSTOM_OEM */ + "OPEN_SHORT", /* AREA_OPEN_SHORT_TUNING */ + "OTP", /* AREA_CUSTOM_OTP */ + "PPDT", /* AREA_PPDT */ + "ROMBOOT_APP_CODE",/* AREA_ROMBOOT_APP_CODE */ + "TOOL_BOOT_CONFIG",/* AREA_TOOL_BOOT_CONFIG */ + + /* please add the declarations above */ + NULL +}; +/** + * @section: Header Content of app config defined + * in firmware file + */ +struct app_config_header { + unsigned short magic_value[4]; + unsigned char checksum[4]; + unsigned char length[2]; + unsigned char build_id[4]; + unsigned char customer_config_id[16]; +}; +/** + * @section: The Partition Descriptor defined + * in firmware file + */ +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; +/** + * @section: Structure for the Data Block defined + * in firmware file + */ +struct block_data { + bool available; + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; + unsigned char id; +}; +/** + * @section: Structure for the Parsed Image File + */ +struct image_info { + struct block_data data[AREA_MAX]; +}; +/** + * @section: Header of Image File + * + * Define the header of firmware image file + */ +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; +/** + * @section: Structure for the Parsed iHex File + */ +struct ihex_info { + unsigned int records; + unsigned char *bin; + unsigned int bin_size; + struct block_data block[IHEX_MAX_BLOCKS]; +}; + +/** + * syna_tcm_get_flash_area_string() + * + * Return the string ID of target area in the flash memory + * + * @param + * [ in] area: target flash area + * + * @return + * the string ID + */ +static inline char *syna_tcm_get_flash_area_string(enum flash_area area) +{ + if (area < AREA_MAX) + return (char *)flash_area_str[area]; + else + return ""; +} + +/** + * syna_tcm_save_flash_block_data() + * + * Save the block data of flash memory to the corresponding structure. + * + * @param + * [out] image_info: image info used for storing the block data + * [ in] area: target area + * [ in] content: content of data + * [ in] flash_addr: offset of block data + * [ in] size: size of block data + * [ in] checksum: checksum of block data + * + * @return + * on success, return 0; otherwise, negative value on error. + */ +static int syna_tcm_save_flash_block_data(struct image_info *image_info, + enum flash_area area, const unsigned char *content, + unsigned int offset, unsigned int size, unsigned int checksum) +{ + if (!image_info) { + LOGE("Invalid image_info\n"); + return _EINVAL; + } + + if (area >= AREA_MAX) { + LOGE("Invalid flash area\n"); + return _EINVAL; + } + + if (checksum != CRC32((const char *)content, size)) { + LOGE("%s checksum error, in image: 0x%x (0x%x)\n", + AREA_ID_STR(area), checksum, + CRC32((const char *)content, size)); + return _EINVAL; + } + image_info->data[area].size = size; + image_info->data[area].data = content; + image_info->data[area].flash_addr = offset; + image_info->data[area].id = (unsigned char)area; + image_info->data[area].available = true; + + LOGI("%s area - address:0x%08x (%d), size:%d\n", + AREA_ID_STR(area), offset, offset, size); + + return 0; +} + +/** + * syna_tcm_get_flash_area_id() + * + * Return the corresponding ID of flash area based on the given string + * + * @param + * [ in] str: string to look for + * + * + * @return + * if matching, return the corresponding ID; otherwise, return AREA_MAX. + */ +static enum flash_area syna_tcm_get_flash_area_id(char *str) +{ + int area; + char *target; + unsigned int len; + + for (area = AREA_MAX - 1; area >= 0; area--) { + target = AREA_ID_STR(area); + len = syna_pal_str_len(target); + + if (syna_pal_str_cmp(str, target, len) == 0) + return area; + } + + LOGW("Un-defined area string, %s\n", str); + return AREA_MAX; +} + +/** + * syna_tcm_parse_fw_image() + * + * Parse and analyze the information of each areas from the given + * firmware image. + * + * @param + * [ in] image: image file given + * [ in] image_info: data blob stored the parsed data from an image file + * + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_fw_image(const unsigned char *image, + struct image_info *image_info) +{ + int retval = 0; + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct area_descriptor *descriptor; + const unsigned char *content; + enum flash_area target_area; + + if (!image) { + LOGE("No image data\n"); + return _EINVAL; + } + + if (!image_info) { + LOGE("Invalid image_info blob\n"); + return _EINVAL; + } + + syna_pal_mem_set(image_info, 0x00, sizeof(struct image_info)); + + header = (struct image_header *)image; + + magic_value = syna_pal_le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE("Invalid image file magic value\n"); + return _EINVAL; + } + + offset = sizeof(struct image_header); + num_of_areas = syna_pal_le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = syna_pal_le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = syna_pal_le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = syna_pal_le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = syna_pal_le4_to_uint(descriptor->flash_addr_words); + flash_addr = flash_addr * 2; + checksum = syna_pal_le4_to_uint(descriptor->checksum); + + target_area = syna_tcm_get_flash_area_id( + (char *)descriptor->id_string); + + retval = syna_tcm_save_flash_block_data(image_info, + target_area, + content, + flash_addr, + length, + checksum); + if (retval < 0) + return _EINVAL; + } + + return 0; +} + + +/** + * syna_tcm_parse_ihex_line() + * + * Parse a line in the ihex file and convert into an actual data + * + * @param + * [ in] line: a line of string stored in the ihex file + * [out] count: size of actual data + * [out] addr: address of data located + * [out] type: the type of data belonging + * [out] buf: a buffer to store the converted data + * [int] buf_size: size of buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_ihex_line(char *line, unsigned int *count, + unsigned int *addr, unsigned int *type, unsigned char *buf, + unsigned int buf_size) +{ + const int OFFSET_COUNT = 1; + const int SIZE_COUNT = 2; + const int OFFSET_ADDR = OFFSET_COUNT + SIZE_COUNT; + const int SIZE_ADDR = 4; + const int OFFSET_TYPE = OFFSET_ADDR + SIZE_ADDR; + const int SIZE_TYPE = 2; + const int OFFSET_DATA = OFFSET_TYPE + SIZE_TYPE; + const int SIZE_DATA = 2; + unsigned int pos; + + if (!line) { + LOGE("No string line\n"); + return _EINVAL; + } + + if ((!buf) || (buf_size == 0)) { + LOGE("Invalid temporary data buffer\n"); + return _EINVAL; + } + + *count = syna_pal_hex_to_uint( + line + OFFSET_COUNT, 2); + *addr = syna_pal_hex_to_uint( + line + OFFSET_ADDR, SIZE_ADDR); + *type = syna_pal_hex_to_uint( + line + OFFSET_TYPE, SIZE_TYPE); + + if (*count > buf_size) { + LOGE("Data size mismatched, required:%d, given:%d\n", + *count, buf_size); + return _EINVAL; + } + + for (pos = 0; pos < *count; pos++) + buf[pos] = (unsigned char)syna_pal_hex_to_uint( + line + (((int)(pos << 1)) + OFFSET_DATA), + SIZE_DATA); + + return 0; +} + +/** + * syna_tcm_parse_fw_ihex() + * + * Based on the firmware ihex file given , parse and convert into a binary + * firmware data to update. + * + * @param + * [ in] ihex: original ihex file + * [ in] ihex_size: size of given file + * [ in] ihex_info: data blob stored the parsed data from an ihex file. + * assume the data buffer inside was allocated + * [ in] len_per_line: length for a useful data in a line + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_fw_ihex(const char *ihex, int ihex_size, + struct ihex_info *ihex_info, const unsigned int len_per_line) +{ + int retval; + unsigned int pos; + unsigned int record; + char *tmp = NULL; + unsigned int count; + unsigned int type; + unsigned char data[32] = { 0 }; + unsigned int addr; + unsigned int offset; + unsigned int prev_addr; + unsigned int block_idx = 0; + + if (!ihex) { + LOGE("No ihex data\n"); + return _EINVAL; + } + + if (!ihex_info) { + LOGE("Invalid ihex_info blob\n"); + return _EINVAL; + } + + if ((!ihex_info->bin) || (ihex_info->bin_size == 0)) { + LOGE("Invalid ihex_info->data\n"); + return _EINVAL; + } + + tmp = syna_pal_mem_alloc(len_per_line + 1, sizeof(char)); + if (!tmp) { + LOGE("Fail to allocate temporary buffer\n"); + return _ENOMEM; + } + + offset = 0; + addr = 0; + pos = 0; + prev_addr = 0; + + ihex_info->records = ihex_size / len_per_line; + LOGD("records = %d\n", ihex_info->records); + + for (record = 0; record < ihex_info->records; record++) { + pos = record * len_per_line; + if ((char)ihex[pos] != ':') { + LOGE("Invalid string maker at pos %d, marker:%c\n", + pos, (char)ihex[pos]); + goto exit; + } + + retval = syna_pal_mem_cpy(tmp, len_per_line, + &ihex[pos], ihex_size - pos, len_per_line); + if (retval < 0) { + LOGE("Fail to copy a line at pos %d\n", pos); + goto exit; + } + + retval = syna_tcm_parse_ihex_line(tmp, &count, &addr, &type, + data, sizeof(data)); + if (retval < 0) { + LOGE("Fail to parse line at pos %d\n", pos); + goto exit; + } + + if ((((prev_addr + 2) & 0xFFFF) != addr) && (type == 0x00)) { + block_idx = (record == 0) ? 0 : block_idx + 1; + if (block_idx >= IHEX_MAX_BLOCKS) { + LOGE("Invalid block index\n"); + goto exit; + } + + ihex_info->block[block_idx].flash_addr = + addr + offset; + ihex_info->block[block_idx].data = + &ihex_info->bin[addr + offset]; + ihex_info->block[block_idx].available = true; + } + + if (type == 0x00) { + + prev_addr = addr; + addr += offset; + + if (addr >= ihex_info->bin_size) { + LOGE("No enough size for data0 addr:0x%x(%d)\n", + addr, addr); + goto exit; + } + ihex_info->bin[addr++] = data[0]; + + if (addr >= ihex_info->bin_size) { + LOGE("No enough size for data1 addr:0x%x(%d)\n", + addr, addr); + goto exit; + } + ihex_info->bin[addr++] = data[1]; + + ihex_info->block[block_idx].size += 2; + + } else if (type == 0x02) { + offset = (data[0] << 8) + data[1]; + offset <<= 4; + } + } + + ihex_info->bin_size = addr; /* the actual size after data reordering */ + LOGN("Size of firmware binary data = %d\n", ihex_info->bin_size); + +exit: + syna_pal_mem_free((void *)tmp); + + return 0; +} + + +#endif /* end of _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ */ diff --git a/tcm/synaptics_touchcom_func_reflash.c b/tcm/synaptics_touchcom_func_reflash.c new file mode 100644 index 0000000..af67b4a --- /dev/null +++ b/tcm/synaptics_touchcom_func_reflash.c @@ -0,0 +1,2074 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_reflash.c + * + * This file implements the fw reflash related functions of TouchBoot. + * The declarations are available in synaptics_touchcom_func_reflash.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_reflash.h" + +/** + * @section: Reflash relevant definitions + * + */ +#define FLASH_READ_DELAY_MS (10) +#define FLASH_WRITE_DELAY_MS (20) +#define FLASH_ERASE_DELAY_MS (500) + +#define BOOT_CONFIG_SIZE 8 +#define BOOT_CONFIG_SLOTS 16 + +#define DO_NONE (0) +#define DO_UPDATE (1) + +/** + * syna_tcm_set_up_flash_access() + * + * Enter the bootloader fw if not in the mode. + * Besides, get the necessary parameters in boot info. + * + * @param + * [ in] tcm_dev: the device handle + * [out] reflash_data: data blob for reflash + * + * @return + * Result of image file comparison + */ +static int syna_tcm_set_up_flash_access(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data) +{ + int retval; + unsigned int temp; + struct tcm_identification_info id_info; + struct tcm_boot_info *boot_info; + unsigned int wr_chunk; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + LOGI("Set up flash access\n"); + + retval = syna_tcm_identify(tcm_dev, &id_info); + if (retval < 0) { + LOGE("Fail to do identification\n"); + return retval; + } + + /* switch to bootloader mode */ + if (IS_APP_FW_MODE(id_info.mode)) { + LOGI("Prepare to enter bootloader mode\n"); + + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE("Fail to enter bootloader mode\n"); + return retval; + } + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter bootloader mode (current: 0x%x)\n", + tcm_dev->dev_mode); + return retval; + } + + boot_info = &tcm_dev->boot_info; + + /* get boot info to set up the flash access */ + retval = syna_tcm_get_boot_info(tcm_dev, boot_info); + if (retval < 0) { + LOGE("Fail to get boot info at mode 0x%x\n", + id_info.mode); + return retval; + } + + wr_chunk = tcm_dev->max_wr_size; + + temp = boot_info->write_block_size_words; + reflash_data->write_block_size = temp * 2; + + LOGI("Write block size: %d (words size: %d)\n", + reflash_data->write_block_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words); + reflash_data->page_size = temp * 2; + + LOGI("Erase page size: %d (words size: %d)\n", + reflash_data->page_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size); + reflash_data->max_write_payload_size = temp; + + LOGI("Max write flash data size: %d\n", + reflash_data->max_write_payload_size); + + if (reflash_data->write_block_size > (wr_chunk - 9)) { + LOGE("Write block size, %d, greater than chunk space, %d\n", + reflash_data->write_block_size, (wr_chunk - 9)); + return _EINVAL; + } + + if (reflash_data->write_block_size == 0) { + LOGE("Invalid write block size %d\n", + reflash_data->write_block_size); + return _EINVAL; + } + + if (reflash_data->page_size == 0) { + LOGE("Invalid erase page size %d\n", + reflash_data->page_size); + return _EINVAL; + } + + return 0; +} + +/** + * syna_tcm_compare_image_id_info() + * + * Compare the ID information between device and the image file, + * and then determine the area to be updated. + * The function should be called after parsing the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * + * @return + * Blocks to be updated + */ +int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data) +{ + enum update_area result; + unsigned int idx; + unsigned int image_fw_id; + unsigned int device_fw_id; + unsigned char *image_config_id; + unsigned char *device_config_id; + struct app_config_header *header; + const unsigned char *app_config_data; + struct block_data *app_config; + + result = UPDATE_NONE; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data\n"); + return _EINVAL; + } + + app_config = &reflash_data->image_info.data[AREA_APP_CONFIG]; + + if (app_config->size < sizeof(struct app_config_header)) { + LOGE("Invalid application config in image file\n"); + return _EINVAL; + } + + app_config_data = app_config->data; + header = (struct app_config_header *)app_config_data; + + image_fw_id = syna_pal_le4_to_uint(header->build_id); + device_fw_id = tcm_dev->packrat_number; + + LOGN("Device firmware ID: %d, image build id: %d\n", + device_fw_id, image_fw_id); + + if (image_fw_id > device_fw_id) { + LOGN("Image build ID newer than device fw ID\n"); + result = UPDATE_FIRMWARE_CONFIG; + goto exit; + } else { + result = UPDATE_NONE; + goto exit; + } + + image_config_id = header->customer_config_id; + device_config_id = tcm_dev->app_info.customer_config_id; + + for (idx = 0; idx < MAX_SIZE_CONFIG_ID; idx++) { + if (image_config_id[idx] != device_config_id[idx]) { + LOGN("Different Config ID\n"); + result = UPDATE_CONFIG_ONLY; + goto exit; + } + } + + result = UPDATE_NONE; + +exit: + switch (result) { + case UPDATE_FIRMWARE_CONFIG: + LOGN("Update firmware and config\n"); + break; + case UPDATE_CONFIG_ONLY: + LOGN("Update config only\n"); + break; + case UPDATE_NONE: + default: + LOGN("No need to do reflash\n"); + break; + } + + return (int)result; +} + +/** + * syna_tcm_check_flash_boot_config() + * + * Check whether the same flash address of boot config in between the device + * and the image file. + * + * @param + * [ in] boot_config: block data of boot_config from image file + * [ in] boot_info: data of boot info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_boot_config(struct block_data *boot_config, + struct tcm_boot_info *boot_info, unsigned int block_size) +{ + unsigned int start_block; + unsigned int image_addr; + unsigned int device_addr; + + if (!boot_config) { + LOGE("Invalid boot_config block data\n"); + return _EINVAL; + } + + if (!boot_info) { + LOGE("Invalid boot_info\n"); + return _EINVAL; + } + + if (boot_config->size < BOOT_CONFIG_SIZE) { + LOGE("No valid BOOT_CONFIG size, %d, in image file\n", + boot_config->size); + return _EINVAL; + } + + image_addr = boot_config->flash_addr; + + LOGD("Boot Config address in image file: 0x%x\n", image_addr); + + start_block = VALUE(boot_info->boot_config_start_block); + device_addr = start_block * block_size; + + LOGD("Boot Config address in device: 0x%x\n", device_addr); + + return DO_NONE; +} + +/** + * syna_tcm_check_flash_app_config() + * + * Check whether the same flash address of app config in between the + * device and the image file. + * + * @param + * [ in] app_config: block data of app_config from image file + * [ in] app_info: data of application info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_config(struct block_data *app_config, + struct tcm_application_info *app_info, unsigned int block_size) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + + if (!app_config) { + LOGE("Invalid app_config block data\n"); + return _EINVAL; + } + + if (!app_info) { + LOGE("Invalid app_info\n"); + return _EINVAL; + } + + if (app_config->size == 0) { + LOGD("No APP_CONFIG in image file\n"); + return DO_NONE; + } + + image_addr = app_config->flash_addr; + image_size = app_config->size; + + LOGD("App Config address in image file: 0x%x, size: %d\n", + image_addr, image_size); + + temp = VALUE(app_info->app_config_start_write_block); + device_addr = temp * block_size; + device_size = VALUE(app_info->app_config_size); + + LOGD("App Config address in device: 0x%x, size: %d\n", + device_addr, device_size); + + if (device_addr == 0 && device_size == 0) + return DO_UPDATE; + + if (image_addr != device_addr) + LOGW("App Config address mismatch, image:0x%x, dev:0x%x\n", + image_addr, device_addr); + + if (image_size != device_size) + LOGW("App Config address size mismatch, image:%d, dev:%d\n", + image_size, device_size); + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_disp_config() + * + * Check whether the same flash address of display config in between the + * device and the image file. + * + * @param + * [ in] disp_config: block data of disp_config from image file + * [ in] boot_info: data of boot info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_disp_config(struct block_data *disp_config, + struct tcm_boot_info *boot_info, unsigned int block_size) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + + if (!disp_config) { + LOGE("Invalid disp_config block data\n"); + return _EINVAL; + } + + if (!boot_info) { + LOGE("Invalid boot_info\n"); + return _EINVAL; + } + + /* disp_config area may not be included in all product */ + if (disp_config->size == 0) { + LOGD("No DISP_CONFIG in image file\n"); + return DO_NONE; + } + + image_addr = disp_config->flash_addr; + image_size = disp_config->size; + + LOGD("Disp Config address in image file: 0x%x, size: %d\n", + image_addr, image_size); + + temp = VALUE(boot_info->display_config_start_block); + device_addr = temp * block_size; + + temp = VALUE(boot_info->display_config_length_blocks); + device_size = temp * block_size; + + LOGD("Disp Config address in device: 0x%x, size: %d\n", + device_addr, device_size); + + if (image_addr != device_addr) + LOGW("Disp Config address mismatch, image:0x%x, dev:0x%x\n", + image_addr, device_addr); + + if (image_size != device_size) + LOGW("Disp Config address size mismatch, image:%d, dev:%d\n", + image_size, device_size); + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_app_code() + * + * Check whether the valid size of app firmware in the image file + * + * @param + * [ in] app_code: block data of app_code from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_code(struct block_data *app_code) +{ + if (!app_code) { + LOGE("Invalid app_code block data\n"); + return _EINVAL; + } + + if (app_code->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(app_code->id)); + return _EINVAL; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_openshort() + * + * Check whether the valid size of openshort area in the image file + * + * @param + * [ in] open_short: block data of open_short from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_openshort(struct block_data *open_short) +{ + if (!open_short) { + LOGE("Invalid open_short block data\n"); + return _EINVAL; + } + + /* open_short area may not be included in all product */ + if (open_short->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(open_short->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_app_prod_test() + * + * Check whether the valid size of app prod_test area in the image file + * + * @param + * [ in] prod_test: block data of app_prod_test from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_prod_test(struct block_data *prod_test) +{ + if (!prod_test) { + LOGE("Invalid app_prod_test block data\n"); + return _EINVAL; + } + + /* app_prod_test area may not be included in all product */ + if (prod_test->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(prod_test->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_ppdt() + * + * Check whether the valid size of ppdt area in the image file + * + * @param + * [ in] ppdt: block data of PPDT from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_ppdt(struct block_data *ppdt) +{ + if (!ppdt) { + LOGE("Invalid ppdt block data\n"); + return _EINVAL; + } + + /* open_short area may not be included in all product */ + if (ppdt->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(ppdt->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_block() + * + * Dispatch to the proper helper to ensure the data of associated block area + * is correct in between the device and the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to check + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block) +{ + int retval = 0; + struct tcm_application_info *app_info; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return DO_NONE; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return DO_NONE; + } + + if (!block) { + LOGE("Invalid block data\n"); + return DO_NONE; + } + + app_info = &tcm_dev->app_info; + boot_info = &tcm_dev->boot_info; + + switch (block->id) { + case AREA_APP_CODE: + retval = syna_tcm_check_flash_app_code(block); + break; + case AREA_APP_CONFIG: + retval = syna_tcm_check_flash_app_config(block, app_info, + reflash_data->write_block_size); + break; + case AREA_BOOT_CONFIG: + retval = syna_tcm_check_flash_boot_config(block, boot_info, + reflash_data->write_block_size); + break; + case AREA_DISP_CONFIG: + retval = syna_tcm_check_flash_disp_config(block, boot_info, + reflash_data->write_block_size); + break; + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_check_flash_openshort(block); + break; + case AREA_PROD_TEST: + retval = syna_tcm_check_flash_app_prod_test(block); + break; + case AREA_PPDT: + retval = syna_tcm_check_flash_ppdt(block); + break; + default: + retval = DO_NONE; + break; + } + + return retval; +} + +/** + * syna_tcm_get_flash_data_location() + * + * Return the address and length of the specified data area + * in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: specified area in flash memory + * [out] addr: the flash address of the specified area returned + * [out] len: the size of the specified area returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_get_flash_data_location(struct tcm_dev *tcm_dev, + enum flash_area area, unsigned int *addr, unsigned int *len) +{ + int retval = 0; + unsigned char resp_code; + unsigned char payload; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + switch (area) { + case AREA_CUSTOM_LCM: + payload = FLASH_LCM_DATA; + break; + case AREA_CUSTOM_OEM: + payload = FLASH_OEM_DATA; + break; + case AREA_PPDT: + payload = FLASH_PPDT_DATA; + break; + case AREA_FORCE_TUNING: + payload = FLASH_FORCE_CALIB_DATA; + break; + case AREA_OPEN_SHORT_TUNING: + payload = FLASH_OPEN_SHORT_TUNING_DATA; + break; + default: + LOGE("Invalid flash area %d\n", area); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_DATA_LOCATION, + &payload, + sizeof(payload), + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_DATA_LOCATION); + goto exit; + } + + if (tcm_dev->resp_buf.data_length != 4) { + LOGE("Invalid data length %d\n", + tcm_dev->resp_buf.data_length); + retval = _EINVAL; + goto exit; + } + + *addr = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[0]); + *len = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[2]); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_reflash_send_command() + * + * Helper to wrap up the write_message() function. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: given command code + * [ in] payload: payload data, if any + * [ in] payload_len: length of payload data + * [ in] delay_ms_resp: delay time to get the response of command + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_reflash_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_len, unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in BL mode, 0x%x\n", tcm_dev->dev_mode); + retval = _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + command, + payload, + payload_len, + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", command); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_read_flash() + * + * Implement the bootloader command to read specified data from flash memory. + * + * Reads to the protected bootloader code or application code areas will read + * as 0. If the number of words requested is too large, it may be truncated to + * an defined maximum read size. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] address: the address in flash memory to read + * [out] rd_data: data retrieved + * [ in] rd_len: length of data to be read + * [ in] rd_delay_ms: a short delay after the command executed + * set 'DEFAULT_FLASH_READ_DELAY' to use default, + * which is 10 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash(struct tcm_dev *tcm_dev, + unsigned int address, unsigned char *rd_data, + unsigned int rd_len, unsigned int rd_delay_ms) +{ + int retval = 0; + unsigned int length_words; + unsigned int flash_addr_words; + unsigned char out[6] = { 0 }; + unsigned int delay_ms = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid rd_data buffer\n"); + return _EINVAL; + } + + if (address == 0 || rd_len == 0) { + LOGE("Invalid flash address and length\n"); + retval = _EINVAL; + goto exit; + } + + length_words = rd_len / 2; + flash_addr_words = address / 2; + + LOGD("Flash address: 0x%x (words: 0x%x), size: %d (words: %d)\n", + address, flash_addr_words, rd_len, length_words); + + out[0] = (unsigned char)flash_addr_words; + out[1] = (unsigned char)(flash_addr_words >> 8); + out[2] = (unsigned char)(flash_addr_words >> 16); + out[3] = (unsigned char)(flash_addr_words >> 24); + out[4] = (unsigned char)length_words; + out[5] = (unsigned char)(length_words >> 8); + + if (rd_delay_ms == DEFAULT_FLASH_READ_DELAY) + delay_ms = FLASH_READ_DELAY_MS; + else + delay_ms = rd_delay_ms; + + if (delay_ms == RESP_IN_ATTN) { + LOGD("xfer: %d, delay: ATTN-driven\n", length_words); + } else { + delay_ms = (delay_ms * length_words) / 1000; + LOGD("xfer: %d, delay: %d\n", length_words, delay_ms); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_READ_FLASH, + out, + sizeof(out), + delay_ms); + if (retval < 0) { + LOGE("Fail to read flash data from addr 0x%x, size %d\n", + address, rd_len); + goto exit; + } + + if (tcm_dev->resp_buf.data_length != rd_len) { + LOGE("Fail to read requested length %d, rd_len %d\n", + tcm_dev->resp_buf.data_length, rd_len); + retval = _EIO; + goto exit; + } + + retval = syna_pal_mem_cpy(rd_data, + rd_len, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + rd_len); + if (retval < 0) { + LOGE("Fail to copy read data, size %d\n", rd_len); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_boot_config() + * + * Read the data of boot config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_boot_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("BOOT_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->boot_config_start_block); + addr = temp * reflash_data->write_block_size; + length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS; + + if (addr == 0 || length == 0) { + LOGE("BOOT_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("BOOT_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read BOOT_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_app_config() + * + * Read the data of app config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_app_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("APP_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(app_info->app_config_start_write_block); + addr = temp * reflash_data->write_block_size; + length = VALUE(app_info->app_config_size); + + if (addr == 0 || length == 0) { + LOGE("APP_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("APP_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read APP_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_disp_config() + * + * Read the data of display config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_disp_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("DISP_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->display_config_start_block); + addr = temp * reflash_data->write_block_size; + temp = VALUE(boot_info->display_config_length_blocks); + length = temp * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("DISP_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("DISP_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read DISP_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_custom_otp() + * + * Read the data of custom OTP area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_custom_otp(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("CUSTOM_OTP not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->custom_otp_start_block); + addr = temp * reflash_data->write_block_size; + temp = VALUE(boot_info->custom_otp_length_blocks); + length = temp * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("CUSTOM_OTP data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("CUSTOM_OTP address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read CUSTOM_OTP area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_custom_data() + * + * Read the data of custom data in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: address generated by get_flash_data_location() + * [ in] size: size generated by get_flash_data_location() + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_custom_data(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, unsigned int size, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int addr = 0; + unsigned int length = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Custom data not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + addr = address * reflash_data->write_block_size; + length = size * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("Custom data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("Custom data address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read custom data (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} +/** + * syna_tcm_read_flash_area() + * + * Entry function to read in the data of specific area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: flash area to read + * [out] data: buffer storing the retrieved data + * [ in] rd_delay_us: delay time in micro-sec to read words data from flash. + * set 'DEFAULT_FLASH_READ_DELAY' to use default, + * which is 10 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev, + enum flash_area area, struct tcm_buffer *rd_data, + unsigned int rd_delay_us) +{ + int retval; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_reflash_data_blob reflash_data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid data buffer\n"); + return _EINVAL; + } + + switch (area) { + case AREA_CUSTOM_LCM: + case AREA_CUSTOM_OEM: + case AREA_PPDT: + case AREA_FORCE_TUNING: + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_get_flash_data_location(tcm_dev, + area, &addr, &length); + if (retval < 0) { + LOGE("Fail to get data location of 0x%x\n", area); + return retval; + } + break; + default: + break; + } + + retval = syna_tcm_set_up_flash_access(tcm_dev, + &reflash_data); + if (retval < 0) { + LOGE("Fail to set up flash access\n"); + return retval; + } + + syna_tcm_buf_init(&reflash_data.out); + + switch (area) { + case AREA_BOOT_CONFIG: + retval = syna_tcm_read_flash_boot_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get boot config data\n"); + goto exit; + } + break; + case AREA_APP_CONFIG: + retval = syna_tcm_read_flash_app_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get app config data\n"); + goto exit; + } + break; + case AREA_DISP_CONFIG: + retval = syna_tcm_read_flash_disp_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get disp config data\n"); + goto exit; + } + break; + case AREA_CUSTOM_OTP: + retval = syna_tcm_read_flash_custom_otp(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get custom otp data\n"); + goto exit; + } + break; + case AREA_CUSTOM_LCM: + case AREA_CUSTOM_OEM: + case AREA_PPDT: + case AREA_FORCE_TUNING: + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_read_flash_custom_data(tcm_dev, + &reflash_data, addr, length, rd_data, + rd_delay_us); + break; + default: + LOGE("Invalid data area\n"); + retval = _EINVAL; + goto exit; + } + + LOGI("%s read\n", AREA_ID_STR(area)); + + retval = 0; + +exit: + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_APPLICATION_FIRMWARE, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) + LOGE("Fail to go back to application firmware\n"); + + syna_tcm_buf_release(&reflash_data.out); + + return retval; +} + +/** + * syna_tcm_write_flash() + * + * Implement the bootloader command to write specified data to flash memory. + * + * If the length of the data to write is not an integer multiple of words, + * the trailing byte will be discarded. If the length of the data to write + * is not an integer number of write blocks, it will be zero-padded to the + * next write block. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: the address in flash memory to write + * [ in] wr_data: data to write + * [ in] wr_len: length of data to write + * [ in] wr_delay_ms: a short delay after the command executed + * set 'DEFAULT_FLASH_WRITE_DELAY' to use default, + * which is 20 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_write_flash(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, const unsigned char *wr_data, + unsigned int wr_len, unsigned int wr_delay_ms) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + unsigned int delay_ms; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + w_length = tcm_dev->max_wr_size - 8; + + w_length = w_length - (w_length % reflash_data->write_block_size); + + w_length = MIN(w_length, reflash_data->max_write_payload_size); + + offset = 0; + + remaining_length = wr_len; + + syna_tcm_buf_lock(&reflash_data->out); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_buf_alloc(&reflash_data->out, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for buf.out\n"); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + flash_address = address + offset; + block_address = flash_address / reflash_data->write_block_size; + reflash_data->out.buf[0] = (unsigned char)block_address; + reflash_data->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = syna_pal_mem_cpy(&reflash_data->out.buf[2], + reflash_data->out.buf_size - 2, + &wr_data[offset], + wr_len - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy write data ,size: %d\n", + xfer_length); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY) + delay_ms = FLASH_WRITE_DELAY_MS; + else + delay_ms = wr_delay_ms; + + if (delay_ms == RESP_IN_ATTN) { + LOGD("xfer: %d, delay: ATTN-driven\n", xfer_length); + } else { + delay_ms = (delay_ms * xfer_length) / 1000; + LOGD("xfer: %d, delay: %d\n", xfer_length, delay_ms); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_WRITE_FLASH, + reflash_data->out.buf, + xfer_length + 2, + delay_ms); + if (retval < 0) { + LOGE("Fail to write data to flash addr 0x%x, size %d\n", + flash_address, xfer_length + 2); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + syna_tcm_buf_unlock(&reflash_data->out); + + return 0; +} + +/** + * syna_tcm_write_flash_block() + * + * Write data to the target block data area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] area: target block area to write + * [ in] wr_delay_us: delay time in micro-sec to write block data to flash. + * set 'DEFAULT_FLASH_WRITE_DELAY' to use default, + * which is 20 ms; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_write_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int wr_delay_us) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + const unsigned char *data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + data = block->data; + size = block->size; + flash_addr = block->flash_addr; + + LOGD("Write data to %s - address: 0x%x, size: %d\n", + AREA_ID_STR(block->id), flash_addr, size); + + if (size == 0) { + LOGI("No need to update, size = %d\n", size); + goto exit; + } + + retval = syna_tcm_write_flash(tcm_dev, reflash_data, + flash_addr, data, size, wr_delay_us); + if (retval < 0) { + LOGE("Fail to write %s to flash (addr: 0x%x, size: %d)\n", + AREA_ID_STR(block->id), flash_addr, size); + return retval; + } + +exit: + LOGN("%s area written\n", AREA_ID_STR(block->id)); + + return 0; +} + +/** + * syna_tcm_erase_flash() + * + * Implement the bootloader command, which is used to erase the specified + * blocks of flash memory. + * + * Until this command completes, the device may be unresponsive. + * Therefore, this helper is implemented as a blocked function, and the delay + * time is set to 200 ms in default. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: the address in flash memory to read + * [ in] size: size of data to write + * [ in] erase_delay_ms: the delay time to get the resp from mass erase + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, unsigned int size, + unsigned int erase_delay_ms) +{ + int retval; + unsigned int page_start = 0; + unsigned int page_count = 0; + unsigned char out_buf[4] = {0}; + int size_erase_cmd; + + page_start = address / reflash_data->page_size; + + page_count = syna_pal_ceil_div(size, reflash_data->page_size); + + LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n", + page_start, page_start, page_count, page_count); + + if ((page_start > 0xff) || (page_count > 0xff)) { + size_erase_cmd = 4; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)((page_start >> 8) & 0xff); + out_buf[2] = (unsigned char)(page_count & 0xff); + out_buf[3] = (unsigned char)((page_count >> 8) & 0xff); + } else { + size_erase_cmd = 2; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)(page_count & 0xff); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_ERASE_FLASH, + out_buf, + size_erase_cmd, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase data at flash page 0x%x, count %d\n", + page_start, page_count); + return retval; + } + + return 0; +} + +/** + * syna_tcm_erase_flash_block() + * + * Mass erase the target block data area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to erase + * [ in] delay_ms: a short delay after the erase command executed + * set 'DEFAULT_FLASH_ERASE_DELAY' to use default, + * which is 500 ms; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_erase_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int delay_ms) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + flash_addr = block->flash_addr; + + size = block->size; + + if (delay_ms == DEFAULT_FLASH_ERASE_DELAY) + delay_ms = FLASH_ERASE_DELAY_MS; + + LOGD("Erase %s block - address: 0x%x, size: %d\n", + AREA_ID_STR(block->id), flash_addr, size); + + if (size == 0) { + LOGI("No need to erase, size = %d\n", size); + goto exit; + } + + retval = syna_tcm_erase_flash(tcm_dev, reflash_data, + flash_addr, size, delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s data (addr: 0x%x, size: %d)\n", + AREA_ID_STR(block->id), flash_addr, size); + return retval; + } + +exit: + LOGN("%s area erased\n", AREA_ID_STR(block->id)); + + return 0; +} + +/** + * syna_tcm_update_flash_block() + * + * Perform the reflash sequence to the target area + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_update_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int delay_ms) +{ + int retval; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + /* reflash is not needed for the partition */ + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval < 0) { + LOGE("Invalid %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + if (retval == DO_NONE) + return 0; + + LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_erase_flash_block(tcm_dev, + reflash_data, + block, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_write_flash_block(tcm_dev, + reflash_data, + block, + wr_blk_delay_ms); + if (retval < 0) { + LOGE("Fail to write %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + return 0; +} + +/** + * syna_tcm_do_reflash_tddi() + * + * Implement the sequence specific for MODE_TDDI_BOOTLOADER. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: misc. data used for fw update + * [ in] type: the area to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_do_reflash_tddi(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + enum update_area type, unsigned int delay_ms) +{ + int retval = 0; + int idx; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF; + struct image_info *image_info; + struct block_data *block; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data blob\n"); + return _EINVAL; + } + + image_info = &reflash_data->image_info; + + if (tcm_dev->dev_mode != MODE_TDDI_BOOTLOADER) { + LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n", + tcm_dev->dev_mode, MODE_TDDI_BOOTLOADER); + return _EINVAL; + } + + if (type == UPDATE_NONE) + goto exit; + + /* Always mass erase all blocks, before writing the data */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &image_info->data[idx]; + + if (!block->available) + continue; + + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval == DO_NONE) + continue; + + LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_erase_flash_block(tcm_dev, + reflash_data, + block, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + goto exit; + } + } + + /* Write all the data to flash */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &image_info->data[idx]; + + if (!block->available) + continue; + + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval == DO_NONE) + continue; + + LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_write_flash_block(tcm_dev, + reflash_data, + block, + wr_blk_delay_ms); + if (retval < 0) { + LOGE("Fail to update %s area\n", + AREA_ID_STR(block->id)); + goto exit; + } + } + +exit: + return retval; +} + +/** + * syna_tcm_do_reflash_generic() + * + * Implement the generic sequence of fw update in MODE_BOOTLOADER. + * + * Typically, it is applied on most of discrete touch controllers + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: misc. data used for fw update + * [ in] type: the area to update + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_do_reflash_generic(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + enum update_area type, unsigned int wait_delay_ms) +{ + int retval = 0; + struct block_data *block; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data blob\n"); + return _EINVAL; + } + + if (tcm_dev->dev_mode != MODE_BOOTLOADER) { + LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n", + tcm_dev->dev_mode, MODE_BOOTLOADER); + return _EINVAL; + } + + switch (type) { + case UPDATE_FIRMWARE_CONFIG: + block = &reflash_data->image_info.data[AREA_APP_CODE]; + + retval = syna_tcm_update_flash_block(tcm_dev, + reflash_data, + block, + wait_delay_ms); + if (retval < 0) { + LOGE("Fail to update application firmware\n"); + goto exit; + } + case UPDATE_CONFIG_ONLY: + block = &reflash_data->image_info.data[AREA_APP_CONFIG]; + + retval = syna_tcm_update_flash_block(tcm_dev, + reflash_data, + block, + wait_delay_ms); + if (retval < 0) { + LOGE("Fail to update application config\n"); + goto exit; + } + break; + case UPDATE_NONE: + default: + break; + } +exit: + return retval; +} + +/** + * syna_tcm_do_fw_update() + * + * The entry function to perform fw update upon TouchBoot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: binary data to write + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash) +{ + int retval; + enum update_area type = UPDATE_NONE; + struct tcm_reflash_data_blob reflash_data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!image) || (image_size == 0)) { + LOGE("Invalid image data\n"); + return _EINVAL; + } + + LOGN("Prepare to do reflash\n"); + + syna_tcm_buf_init(&reflash_data.out); + + reflash_data.image = image; + reflash_data.image_size = image_size; + syna_pal_mem_set(&reflash_data.image_info, 0x00, + sizeof(struct image_info)); + + retval = syna_tcm_parse_fw_image(image, &reflash_data.image_info); + if (retval < 0) { + LOGE("Fail to parse firmware image\n"); + return retval; + } + + LOGN("Start of reflash\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + if (force_reflash) { + type = UPDATE_FIRMWARE_CONFIG; + goto reflash; + } + + type = (enum update_area)syna_tcm_compare_image_id_info(tcm_dev, + &reflash_data); + + if (type == UPDATE_NONE) + goto exit; + +reflash: + syna_tcm_buf_init(&reflash_data.out); + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_set_up_flash_access(tcm_dev, &reflash_data); + if (retval < 0) { + LOGE("Fail to set up flash access\n"); + goto exit; + } + + /* perform the fw update */ + if (tcm_dev->dev_mode == MODE_BOOTLOADER) { + retval = syna_tcm_do_reflash_generic(tcm_dev, + &reflash_data, + type, + wait_delay_ms); + } else if (tcm_dev->dev_mode == MODE_TDDI_BOOTLOADER) { + retval = syna_tcm_do_reflash_tddi(tcm_dev, + &reflash_data, + type, + wait_delay_ms); + } else { + LOGE("Incorrect bootloader mode, 0x%02x\n", + tcm_dev->dev_mode); + goto reset; + } + + if (retval < 0) { + LOGE("Fail to do firmware update\n"); + goto reset; + } + + LOGN("End of reflash\n"); + + retval = 0; +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&reflash_data.out); + + return retval; +} + diff --git a/tcm/synaptics_touchcom_func_reflash.h b/tcm/synaptics_touchcom_func_reflash.h new file mode 100644 index 0000000..8e90b80 --- /dev/null +++ b/tcm/synaptics_touchcom_func_reflash.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_reflash.h + * + * This file declares relevant functions and structures for TouchBoot. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base_flash.h" + +/** + * @section: Blocks to be updated + */ +enum update_area { + UPDATE_NONE = 0, + UPDATE_FIRMWARE_CONFIG, + UPDATE_CONFIG_ONLY, + UPDATE_ALL_BLOCKS, +}; + +/** + * @section: Data Type in flash memory + */ +enum flash_data { + FLASH_LCM_DATA = 1, + FLASH_OEM_DATA, + FLASH_PPDT_DATA, + FLASH_FORCE_CALIB_DATA, + FLASH_OPEN_SHORT_TUNING_DATA, +}; + +/** + * @section: Specific data blob for reflash + * + * The structure contains various parameters being used in reflash + */ +struct tcm_reflash_data_blob { + /* binary data of an image file */ + const unsigned char *image; + unsigned int image_size; + /* parsed data based on given image file */ + struct image_info image_info; + /* standard information for flash access */ + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + /* temporary buffer during the reflash */ + struct tcm_buffer out; +}; + +/** + * syna_tcm_compare_image_id_info() + * + * Compare the ID information between device and the image file, + * and determine the area to be updated. + * The function should be called after parsing the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * + * @return + * Comparison result + */ +int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data); + +/** + * syna_tcm_read_flash_area() + * + * Entry function to read in the data of specific area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: flash area to read + * [out] data: buffer storing the retrieved data + * [ in] rd_delay_us: delay time in micro-sec to read words data from flash. + * set '0' to use default time, which is 10 us; + * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev, + enum flash_area area, struct tcm_buffer *rd_data, + unsigned int rd_delay_us); + +/** + * syna_tcm_do_fw_update() + * + * The entry function to perform fw update upon TouchBoot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash); + +#endif /* end of _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ */ diff --git a/tcm/synaptics_touchcom_func_romboot.c b/tcm/synaptics_touchcom_func_romboot.c new file mode 100644 index 0000000..e144fa3 --- /dev/null +++ b/tcm/synaptics_touchcom_func_romboot.c @@ -0,0 +1,1586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_romboot.c + * + * This file implements the ROM boot-loader related functions. + * The declarations are available in synaptics_touchcom_func_romboot.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_romboot.h" + +#define JEDEC_STATUS_CHECK_US_MIN 5000 +#define JEDEC_STATUS_CHECK_US_MAX 10000 + +#define BINARY_FILE_MAGIC_VALUE 0xaa55 + +#define ROMBOOT_FLASH_PAGE_SIZE 256 + +/** + * @section: JEDEC flash command set + */ +enum flash_command { + JEDEC_PAGE_PROGRAM = 0x02, + JEDEC_READ_STATUS = 0x05, + JEDEC_WRITE_ENABLE = 0x06, + JEDEC_CHIP_ERASE = 0xc7, +}; + + +/** + * syna_tcm_romboot_send_command() + * + * Helper to send a packet to ROM bootloader. + * + * Please be noted that the given packet must be formatted into the + * specific structure in order to communicate with flash. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] out: data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms_resp: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_send_command(struct tcm_dev *tcm_dev, + unsigned char *out, unsigned int out_size, unsigned char *in, + unsigned int in_size, unsigned int delay_ms_resp) +{ + int retval; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (out_size < sizeof(struct flash_param)) { + LOGE("Invalid size of out data, %d, min. size:%d\n", + out_size, (int)sizeof(struct flash_param)); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED, + out, + out_size, + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send romboot flash command 0x%02x\n", + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED); + goto exit; + } + + LOGD("resp_code: 0x%x, resp length: %d\n", + resp_code, tcm_dev->resp_buf.data_length); + + if ((in == NULL) || (in_size < tcm_dev->resp_buf.data_length)) + goto exit; + + /* copy resp data to caller */ + retval = syna_pal_mem_cpy((unsigned char *)in, + in_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + tcm_dev->resp_buf.data_length); + if (retval < 0) { + LOGE("Fail to copy resp data to caller\n"); + goto exit; + } + +exit: + return retval; +} + + +/** + * syna_tcm_romboot_multichip_send_command() + * + * Send a command code to the ROM bootloader inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command to send + * [ in] out: additional data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *out, + unsigned int out_size, unsigned char *in, + unsigned int in_size, unsigned int delay_ms) +{ + int retval; + unsigned char *payld_buf = NULL; + unsigned int payld_size; + struct flash_param flash_param; + unsigned int offset = (int)sizeof(struct flash_param); + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param)); + + flash_param.read_size[0] = (unsigned char)(in_size & 0xff); + flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff; + + flash_param.command = command; + + payld_size = offset + out_size; + if (flash_param.command != 0x00) + payld_size += 2; + + LOGD("Command: 0x%02x, packet size: %d, wr:%d, rd:%d\n", + command, payld_size, out_size, in_size); + + payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char)); + if (!payld_buf) { + LOGE("Fail to allocate buffer to store flash command\n"); + return _ENOMEM; + } + + if (flash_param.command != 0x00) { + payld_buf[offset] = (unsigned char)out_size; + payld_buf[offset + 1] = (unsigned char)(out_size >> 8); + + if (out_size > 0) { + retval = syna_pal_mem_cpy(&payld_buf[offset + 2], + payld_size - offset - 2, + out, + out_size, + out_size); + if (retval < 0) { + LOGE("Fail to copy payload to payld_buf\n"); + goto exit; + } + } + + LOGD("Packet: %02x %02x %02x %02x %02x %02x %02x %02x\n", + flash_param.byte0, flash_param.byte1, flash_param.byte2, + flash_param.read_size[0], flash_param.read_size[1], + flash_param.command, payld_buf[offset], + payld_buf[offset + 1]); + } + + retval = syna_pal_mem_cpy(payld_buf, payld_size, + &flash_param, sizeof(flash_param), sizeof(flash_param)); + if (retval < 0) { + LOGE("Fail to copy flash_param header to payld_buf\n"); + goto exit; + } + + retval = syna_tcm_romboot_send_command(tcm_dev, + payld_buf, + payld_size, + in, + in_size, + delay_ms); + if (retval < 0) { + LOGE("Fail to write command 0x%x\n", flash_param.command); + goto exit; + } + +exit: + syna_pal_mem_free((void *)payld_buf); + + return retval; +} +/** + * syna_tcm_romboot_multichip_get_resp() + * + * To get the response data from ROM bootloader inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: size to read + * [ in] resp: buffer to store the resp data + * [ in] resp_size: size of resp data + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_resp(struct tcm_dev *tcm_dev, + unsigned int length, unsigned char *resp, + unsigned int resp_size, unsigned int delay_ms) +{ + int retval; + unsigned char *tmp_buf = NULL; + unsigned int xfer_len; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (resp && (resp_size < length)) { + LOGE("Invalid buffer size, len:%d, size:%d\n", + length, resp_size); + return _EINVAL; + } + + xfer_len = length + 2; + + tmp_buf = syna_pal_mem_alloc(xfer_len, sizeof(unsigned char)); + if (!tmp_buf) { + LOGE("Fail to allocate tmp_buf\n"); + return _ENOMEM; + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_NONE, NULL, 0, + tmp_buf, xfer_len, delay_ms); + if (retval < 0) { + LOGE("Fail to get resp, size: %d\n", xfer_len); + goto exit; + } + + if (resp) { + retval = syna_pal_mem_cpy(resp, resp_size, + &tmp_buf[1], xfer_len - 1, length); + if (retval < 0) { + LOGE("Fail to copy resp data\n"); + goto exit; + } + } + +exit: + syna_pal_mem_free((void *)tmp_buf); + + return retval; +} +/** + * syna_tcm_romboot_multichip_get_status() + * + * To poll the status until the completion + * + * @param + * [ in] tcm_dev: the device handle + * [ in] resp_status: response status returned + * [ in] resp_length: response length returned + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_status(struct tcm_dev *tcm_dev, + unsigned char *resp_status, unsigned int *resp_length, + unsigned int delay_ms) +{ + int retval; + unsigned char resp[4] = { 0 }; + int timeout = 0; + int MAX_TIMEOUT = 1000; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_sleep_ms(delay_ms); + + do { + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_NONE, NULL, 0, + resp, 3, delay_ms); + if (retval < 0) { + LOGE("Fail to poll the resp\n"); + goto exit; + } + + LOGD("status: %02x %02x %02x\n", resp[0], resp[1], resp[2]); + + if (resp[0] == 0xff) { + syna_pal_sleep_ms(100); + timeout += 100; + continue; + } else if (resp[0] == 0x01) { + *resp_status = resp[0]; + *resp_length = syna_pal_le2_to_uint(&resp[1]); + goto exit; + } else { + LOGE("Invalid resp, %02x %02x %02x\n", + resp[0], resp[1], resp[2]); + retval = _EIO; + goto exit; + } + + } while (timeout < MAX_TIMEOUT); + + if (timeout >= 500) { + LOGE("Timeout to get the status\n"); + retval = _EIO; + } +exit: + return retval; +} +/** + * syna_tcm_romboot_multichip_write_flash() + * + * Write the given binary data to the flash through the ROM bootloader + * inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for romboot + * [ in] address: the address in flash memory to write + * [ in] wr_data: binary data to write + * [ in] wr_len: length of data to write + * [ in] wr_delay_ms: a short delay after the command executed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_write_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + unsigned int address, const unsigned char *wr_data, + unsigned int wr_len, unsigned int wr_delay_ms) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + unsigned char resp_code = 0; + unsigned int resp_length = 0; + unsigned int delay_ms; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + w_length = tcm_dev->max_wr_size - 16; + + w_length = w_length - (w_length % romboot_data->write_block_size); + + w_length = MIN(w_length, romboot_data->max_write_payload_size); + + offset = 0; + + remaining_length = wr_len; + + syna_tcm_buf_lock(&romboot_data->out); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_buf_alloc(&romboot_data->out, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for buf.out\n"); + goto exit; + } + + flash_address = address + offset; + block_address = flash_address / romboot_data->write_block_size; + + romboot_data->out.buf[0] = (unsigned char)block_address & 0xff; + romboot_data->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = syna_pal_mem_cpy(&romboot_data->out.buf[2], + romboot_data->out.buf_size - 2, + &wr_data[offset], + wr_len - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy write data ,size: %d\n", + xfer_length); + goto exit; + } + + if (wr_delay_ms == 0) + delay_ms = ROMBOOT_DELAY_MS; + else + delay_ms = wr_delay_ms; + + LOGD("write xfer: %d (remaining: %d)\n", + xfer_length, remaining_length); + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_WRITE_FLASH, + romboot_data->out.buf, + xfer_length + 2, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Fail to write data to flash addr 0x%x, size %d\n", + flash_address, xfer_length + 2); + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_length, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_WRITE_FLASH); + goto exit; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_length); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", + CMD_WRITE_FLASH); + retval = _EIO; + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_resp(tcm_dev, + resp_length, NULL, 0, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the boot info packet\n"); + goto exit; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + retval = 0; + +exit: + syna_tcm_buf_unlock(&romboot_data->out); + + return retval; +} +/** + * syna_tcm_romboot_multichip_erase_flash() + * + * Ask the ROM bootloader to erase the flash inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] address: the address in flash memory to read + * [ in] size: size of data to write + * [ in] erase_delay_ms: the delay time to get the resp from mass erase + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + unsigned int address, unsigned int size, + unsigned int erase_delay_ms) +{ + int retval; + unsigned int page_start = 0; + unsigned int page_count = 0; + unsigned char out_buf[4] = { 0 }; + unsigned char resp_code = 0; + unsigned int resp_length = 0; + int size_erase_cmd; + + page_start = address / romboot_data->page_size; + + page_count = syna_pal_ceil_div(size, romboot_data->page_size); + + LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n", + page_start, page_start, page_count, page_count); + + if ((page_start > 0xff) || (page_count > 0xff)) { + size_erase_cmd = 4; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)((page_start >> 8) & 0xff); + out_buf[2] = (unsigned char)(page_count & 0xff); + out_buf[3] = (unsigned char)((page_count >> 8) & 0xff); + } else { + size_erase_cmd = 2; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)(page_count & 0xff); + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_ERASE_FLASH, + out_buf, + size_erase_cmd, + NULL, + 0, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase data at 0x%x (page:0x%x, count:%d)\n", + address, page_start, page_count); + return retval; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_length, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_ERASE_FLASH); + return retval; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_length); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", CMD_WRITE_FLASH); + retval = _EIO; + return retval; + } + + return retval; +} + +/** + * syna_tcm_romboot_multichip_get_boot_info() + * + * To request a boot information packet from ROM bootloader inside + * the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int resp_data_len = 0; + unsigned int copy_size; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_GET_BOOT_INFO, NULL, 0, NULL, 0, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to run command 0x%x\n", CMD_GET_BOOT_INFO); + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_data_len, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_GET_BOOT_INFO); + return retval; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_data_len); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", CMD_GET_BOOT_INFO); + retval = _EIO; + return retval; + } + + if (boot_info == NULL) + goto exit; + + copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len); + + retval = syna_tcm_romboot_multichip_get_resp(tcm_dev, + copy_size, (unsigned char *)boot_info, + sizeof(struct tcm_boot_info), ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the boot info packet\n"); + return retval; + } + +exit: + return retval; +} +/** + * syna_tcm_romboot_preparation() + * + * Perform the preparation before doing firmware update of multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [out] romboot_data: data blob for romboot access + * [ in] is_multichip: flag to indicate a multichip DUT + * + * @return + * Result of image file comparison + */ +static int syna_tcm_romboot_preparation(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, bool is_multichip) +{ + int retval; + unsigned int temp; + struct tcm_boot_info *boot_info; + unsigned int wr_chunk; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!romboot_data) { + LOGE("Invalid romboot data blob\n"); + return _EINVAL; + } + + LOGI("Set up preparation, multi-chip: %s\n", + (is_multichip)?"yes":"no"); + + retval = syna_tcm_identify(tcm_dev, NULL); + if (retval < 0) { + LOGE("Fail to do identification\n"); + return retval; + } + + /* switch to bootloader mode */ + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGI("Prepare to enter bootloader mode\n"); + if (is_multichip) + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_MULTICHIP_TDDI_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + else + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_TDDI_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + + if (retval < 0) { + LOGE("Fail to enter bootloader mode\n"); + return retval; + } + } + /* switch to rom boot mode */ + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGI("Prepare to enter rom boot mode\n"); + + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_ROMBOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE("Fail to enter rom boot mode\n"); + return retval; + } + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Device not in romboot mode\n"); + return _EINVAL; + } + + if (!is_multichip) + return 0; + + boot_info = &tcm_dev->boot_info; + + retval = syna_tcm_romboot_multichip_get_boot_info(tcm_dev, + boot_info); + if (retval < 0) { + LOGE("Fail to get boot info\n"); + return retval; + } + + wr_chunk = tcm_dev->max_wr_size; + + temp = boot_info->write_block_size_words; + romboot_data->write_block_size = temp * 2; + + LOGI("Write block size: %d (words size: %d)\n", + romboot_data->write_block_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words); + romboot_data->page_size = temp * 2; + + LOGI("Erase page size: %d (words size: %d)\n", + romboot_data->page_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size); + romboot_data->max_write_payload_size = temp; + + LOGI("Max write flash data size: %d\n", + romboot_data->max_write_payload_size); + + if (romboot_data->write_block_size > (wr_chunk - 9)) { + LOGE("Write block size, %d, greater than chunk space, %d\n", + romboot_data->write_block_size, (wr_chunk - 9)); + return _EINVAL; + } + + if (romboot_data->write_block_size == 0) { + LOGE("Invalid write block size %d\n", + romboot_data->write_block_size); + return _EINVAL; + } + + if (romboot_data->page_size == 0) { + LOGE("Invalid erase page size %d\n", + romboot_data->page_size); + return _EINVAL; + } + + return 0; +} + +/** + * syna_tcm_romboot_jedec_send_command() + * + * Send a jedec flash commend to the ROM bootloader + * + * @param + * [ in] tcm_dev: the device handle + * [ in] flash_command: flash command to send + * [ in] out: additional data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_send_command(struct tcm_dev *tcm_dev, + unsigned char flash_command, unsigned char *out, + unsigned int out_size, unsigned char *in, unsigned int in_size, + unsigned int delay_ms) +{ + int retval; + unsigned char *payld_buf = NULL; + unsigned int payld_size; + struct flash_param flash_param; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param)); + + flash_param.spi_param = 1; + flash_param.clk_div = 0x19; + + flash_param.read_size[0] = (unsigned char)(in_size & 0xff); + flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff; + + flash_param.command = flash_command; + + payld_size = sizeof(struct flash_param) + out_size; + + LOGD("Flash command: 0x%02x, total size: %d, wr: %d, rd: %d\n", + flash_command, payld_size, out_size, in_size); + LOGD("Packet: %02x %02x %02x %02x %02x %02x\n", + flash_param.byte0, flash_param.byte1, flash_param.byte2, + flash_param.read_size[0], flash_param.read_size[1], + flash_param.command); + + payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char)); + if (!payld_buf) { + LOGE("Fail to allocate buffer to store flash command\n"); + return _ENOMEM; + } + + retval = syna_pal_mem_cpy(payld_buf, payld_size, + &flash_param, sizeof(flash_param), sizeof(flash_param)); + if (retval < 0) { + LOGE("Fail to copy flash_param header to payld_buf\n"); + goto exit; + } + + if (out && (out_size > 0)) { + retval = syna_pal_mem_cpy(payld_buf + sizeof(flash_param), + payld_size - sizeof(flash_param), + out, out_size, out_size); + if (retval < 0) { + LOGE("Fail to copy data to payld_buf\n"); + goto exit; + } + } + + retval = syna_tcm_romboot_send_command(tcm_dev, + payld_buf, + payld_size, + in, + in_size, + delay_ms); + if (retval < 0) { + LOGE("Fail to write flash command 0x%x\n", flash_command); + goto exit; + } + +exit: + syna_pal_mem_free((void *)payld_buf); + + return retval; +} + +/** + * syna_tcm_romboot_jedec_get_status() + * + * Use jedec command to poll the flash status until the completion + * + * @param + * [ in] tcm_dev: the device handle + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_get_status(struct tcm_dev *tcm_dev, + unsigned int delay_ms) +{ + int retval; + int idx; + unsigned char status; + int STATUS_CHECK_RETRY = 50; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + for (idx = 0; idx < STATUS_CHECK_RETRY; idx++) { + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_READ_STATUS, + NULL, + 0, + &status, + sizeof(status), + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_READ_STATUS\n"); + return retval; + } + + syna_pal_sleep_us(JEDEC_STATUS_CHECK_US_MIN, + JEDEC_STATUS_CHECK_US_MAX); + /* once completed, status = 0 */ + if (!status) + break; + } + + if (status) + retval = _EIO; + else + retval = status; + + return retval; +} + +/** + * syna_tcm_romboot_jedec_erase_flash() + * + * Ask the ROM bootloader to erase the flash by using the jedec command + * + * @param + * [ in] tcm_dev: the device handle + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_erase_flash(struct tcm_dev *tcm_dev, + unsigned int delay_ms) +{ + int retval; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_WRITE_ENABLE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + return retval; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_CHIP_ERASE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + return retval; + } + + retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms); + if (retval < 0) + LOGE("Fail to get correct status, retval = %d\n", retval); + + return retval; +} + + /** + * syna_tcm_romboot_jedec_write_flash() + * + * Write the given binary data to the flash through the ROM bootloader + * by using the jedec command + * + * @param + * [ in] tcm_dev: the device handle + * [ in] address: the address in flash memory to write + * [ in] data: binary data to write + * [ in] data_size: size of binary data + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_write_flash(struct tcm_dev *tcm_dev, + unsigned int address, const unsigned char *data, + unsigned int data_size, unsigned int delay_ms) +{ + int retval = 0; + unsigned int offset; + unsigned char buf[ROMBOOT_FLASH_PAGE_SIZE + 3]; + unsigned int remaining_length; + unsigned int xfer_length; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_size == 0)) { + LOGE("Invalid image data, no data available\n"); + return _EINVAL; + } + + remaining_length = data_size; + + offset = 0; + + while (remaining_length) { + if (remaining_length > ROMBOOT_FLASH_PAGE_SIZE) + xfer_length = ROMBOOT_FLASH_PAGE_SIZE; + else + xfer_length = remaining_length; + + syna_pal_mem_set(buf, 0x00, sizeof(buf)); + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_WRITE_ENABLE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + goto exit; + } + + buf[0] = (unsigned char)((address + offset) >> 16); + buf[1] = (unsigned char)((address + offset) >> 8); + buf[2] = (unsigned char)(address + offset); + + retval = syna_pal_mem_cpy(&buf[3], + sizeof(buf) - 3, + &data[offset], + data_size - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy data to write, size: %d\n", + xfer_length); + goto exit; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_PAGE_PROGRAM, + buf, + sizeof(buf), + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write data to addr 0x%x (offset: %x)\n", + address + offset, offset); + LOGE("Remaining data %d\n", + remaining_length); + goto exit; + } + + retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms); + if (retval < 0) { + LOGE("Fail to get correct status, retval = %d\n", + retval); + goto exit; + } + offset += xfer_length; + remaining_length -= xfer_length; + } + +exit: + return retval; +} + + +/** + * syna_tcm_romboot_erase_flash() + * + * The entry function to perform mass erase + * + * @param + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] blk: the block in flash memory to erase + * [ in] delay_ms: delay time to get the response + * [ in] is_multichip: use multi-chip command packet instead + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + struct block_data *blk, unsigned int delay_ms, + bool is_multichip) +{ + int retval; + + if (!tcm_dev || !romboot_data || !blk) + return _EINVAL; + + if (is_multichip) + retval = syna_tcm_romboot_multichip_erase_flash(tcm_dev, + romboot_data, blk->flash_addr, blk->size, + delay_ms); + else + retval = syna_tcm_romboot_jedec_erase_flash(tcm_dev, + delay_ms); + + return retval; +} + +/** + * syna_tcm_romboot_write_flash() + * + * The entry function to write hex data to flash + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] blk: the block in flash memory to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: use multi-chip command packet instead + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_write_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + struct block_data *blk, unsigned int delay_ms, + bool is_multichip) +{ + int retval; + + if (!tcm_dev || !romboot_data || !blk) + return _EINVAL; + + if (is_multichip) + retval = syna_tcm_romboot_multichip_write_flash(tcm_dev, + romboot_data, blk->flash_addr, blk->data, + blk->size, delay_ms); + else + retval = syna_tcm_romboot_jedec_write_flash(tcm_dev, + blk->flash_addr, blk->data, blk->size, + delay_ms); + + return retval; +} + + +/** + * syna_tcm_romboot_do_ihex_update() + * + * The entry function to perform ihex update upon ROM Boot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] ihex: ihex data to write + * [ in] ihex_size: size of ihex data + * [ in] flash_size: size for temporary buffer allocation, which used + * to re-order the flash data. + * in general, (ihex_size + 4K) is preferred size + * [ in] len_per_line: length per line in the ihex file + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: flag to indicate a multi-chip product used + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev, + const unsigned char *ihex, unsigned int ihex_size, + unsigned int flash_size, unsigned int len_per_line, + unsigned int delay_ms, bool is_multichip) +{ + int retval; + struct tcm_romboot_data_blob romboot_data; + struct ihex_info *ihex_info = NULL; + struct block_data *block; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_delay_ms = delay_ms & 0xFFFF; + unsigned short *header; + int idx; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!ihex) || (ihex_size == 0)) { + LOGE("Invalid ihex data\n"); + return _EINVAL; + } + + if (flash_size == 0) + flash_size = ihex_size + 4096; + + romboot_data.bdata = ihex; + romboot_data.bdata_size = ihex_size; + syna_pal_mem_set(&romboot_data.ihex_info, 0x00, + sizeof(struct ihex_info)); + + ihex_info = &romboot_data.ihex_info; + + ihex_info->bin = syna_pal_mem_alloc(flash_size, + sizeof(unsigned char)); + if (!ihex_info->bin) { + LOGE("Fail to allocate buffer for ihex data\n"); + + return _ENOMEM; + } + + ihex_info->bin_size = flash_size; + + syna_tcm_buf_init(&romboot_data.out); + + LOGN("Parse ihex file\n"); + + /* parse ihex file */ + retval = syna_tcm_parse_fw_ihex((const char *)ihex, + ihex_size, ihex_info, len_per_line); + if (retval < 0) { + LOGE("Fail to parse firmware ihex file\n"); + goto exit; + } + + if (!is_multichip) { + header = (unsigned short *)ihex_info->block[0].data; + if (*header != BINARY_FILE_MAGIC_VALUE) { + LOGE("Incorrect image header 0x%04X\n", *header); + goto exit; + } + } + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_romboot_preparation(tcm_dev, + &romboot_data, + is_multichip); + if (retval < 0) { + LOGE("Fail to do preparation\n"); + goto reset; + } + + LOGN("Start of ihex update\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) { + + block = &ihex_info->block[idx]; + + if (!block->available) + continue; + + if (block->size == 0) + continue; + + /* for single-chip, mass erase will affect the + * entire flash memory, so it just takes once + */ + if ((idx != 0) && (!is_multichip)) + break; + + retval = syna_tcm_romboot_erase_flash(tcm_dev, + &romboot_data, + block, + erase_delay_ms, + is_multichip); + if (retval < 0) { + LOGE("Fail to erase flash\n"); + goto reset; + } + } + + LOGN("Flash erased\n"); + LOGN("Start to write all data to flash\n"); + + for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) { + + block = &ihex_info->block[idx]; + + if (!block->available) + continue; + + LOGD("block:%d, addr:0x%x, size:%d\n", + idx, block->flash_addr, block->size); + + if (block->size == 0) + continue; + + retval = syna_tcm_romboot_write_flash(tcm_dev, + &romboot_data, + block, + wr_delay_ms, + is_multichip); + if (retval < 0) { + LOGE("Fail to write data to addr 0x%x, size:%d\n", + block->flash_addr, block->size); + goto reset; + } + + LOGI("Data written, size:%d\n", block->size); + } + + LOGN("End of ihex update\n"); + + retval = 0; + +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + syna_pal_mem_free((void *)ihex_info->bin); + + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&romboot_data.out); + + return retval; +} + +/** + * syna_tcm_romboot_do_multichip_reflash() + * + * The entry function to perform fw update in multi-chip product. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash) +{ + int retval; + int idx; + struct tcm_romboot_data_blob romboot_data; + struct block_data *block; + struct app_config_header *header; + unsigned int image_fw_id; + unsigned int erase_delay_ms = (wait_delay_ms >> 16) & 0xFFFF; + unsigned int wr_delay_ms = wait_delay_ms & 0xFFFF; + bool has_tool_boot_cfg = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!image) || (image_size == 0)) { + LOGE("Invalid image data\n"); + return _EINVAL; + } + + LOGN("Prepare to do reflash\n"); + + syna_pal_mem_set(&romboot_data, 0x00, + sizeof(struct tcm_romboot_data_blob)); + + syna_tcm_buf_init(&romboot_data.out); + + romboot_data.bdata = image; + romboot_data.bdata_size = image_size; + + syna_tcm_buf_init(&romboot_data.out); + + retval = syna_tcm_parse_fw_image(image, &romboot_data.image_info); + if (retval < 0) { + LOGE("Fail to parse firmware image\n"); + retval = _EINVAL; + goto exit; + } + + block = &romboot_data.image_info.data[AREA_APP_CONFIG]; + if (block->size < sizeof(struct app_config_header)) { + LOGE("Invalid application config in image file\n"); + retval = _EINVAL; + goto exit; + } + header = (struct app_config_header *)block->data; + + image_fw_id = syna_pal_le4_to_uint(header->build_id); + + LOGN("Device firmware ID: %d, image build id: %d\n", + tcm_dev->packrat_number, image_fw_id); + + if ((image_fw_id <= tcm_dev->packrat_number) && !force_reflash) { + LOGN("No need to do reflash\n"); + retval = 0; + goto exit; + } + + block = &romboot_data.image_info.data[AREA_TOOL_BOOT_CONFIG]; + has_tool_boot_cfg = block->available; + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_romboot_preparation(tcm_dev, &romboot_data, true); + if (retval < 0) { + LOGE("Fail to do preparation\n"); + goto reset; + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Incorrect device mode 0x%02x, expected:0x%02x\n", + tcm_dev->dev_mode, MODE_ROMBOOTLOADER); + retval = _EINVAL; + goto reset; + } + + LOGN("Start of reflash\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + /* Traverse through all blocks in the image file, + * then erase the corresponding block area + */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &romboot_data.image_info.data[idx]; + + if (!block->available) + continue; + + if (idx == AREA_ROMBOOT_APP_CODE) + continue; + + if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg) + continue; + + LOGD("Erase %s block - address: 0x%x (%d), size: %d\n", + AREA_ID_STR(block->id), block->flash_addr, + block->flash_addr, block->size); + + if (block->size == 0) + continue; + + if (erase_delay_ms == DEFAULT_FLASH_ERASE_DELAY) + erase_delay_ms = ROMBOOT_DELAY_MS; + + retval = syna_tcm_romboot_erase_flash(tcm_dev, + &romboot_data, + block, + erase_delay_ms, + true); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + goto reset; + } + + LOGN("%s partition erased\n", AREA_ID_STR(block->id)); + } + + /* Traverse through all blocks in the image file, + * and then update the corresponding block area + */ + for (idx = 0; idx < AREA_MAX; idx++) { + + LOGD("Prepare to update %s partition\n", AREA_ID_STR(idx)); + + block = &romboot_data.image_info.data[idx]; + + if (!block->available) + continue; + + if (idx == AREA_ROMBOOT_APP_CODE) + continue; + + if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg) + continue; + + LOGD("Write data to %s - address: 0x%x (%d), size: %d\n", + AREA_ID_STR(block->id), block->flash_addr, + block->flash_addr, block->size); + + if (block->size == 0) + continue; + + if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY) + wr_delay_ms = ROMBOOT_DELAY_MS; + + retval = syna_tcm_romboot_write_flash(tcm_dev, + &romboot_data, + block, + wr_delay_ms, + true); + + if (retval < 0) { + LOGE("Fail to update %s partition, size: %d\n", + AREA_ID_STR(block->id), block->size); + goto reset; + } + + LOGN("%s written\n", AREA_ID_STR(block->id)); + } + + LOGN("End of reflash\n"); + + retval = 0; +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&romboot_data.out); + + return retval; +} + + +/** + * syna_tcm_get_romboot_info() + * + * Implement the bootloader command code, which is used to request a + * RomBoot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rom_boot_info: the romboot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev, + struct tcm_romboot_info *rom_boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int copy_size = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_ROMBOOT_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_ROMBOOT_INFO); + goto exit; + } + + if (rom_boot_info == NULL) + goto exit; + + copy_size = MIN(sizeof(struct tcm_romboot_info), + tcm_dev->resp_buf.data_length); + + /* copy romboot_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)rom_boot_info, + sizeof(struct tcm_romboot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy romboot info to caller\n"); + goto exit; + } + +exit: + return retval; +} + + + diff --git a/tcm/synaptics_touchcom_func_romboot.h b/tcm/synaptics_touchcom_func_romboot.h new file mode 100644 index 0000000..812caf9 --- /dev/null +++ b/tcm/synaptics_touchcom_func_romboot.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_touchcom_func_romboot.h + * + * This file declares relevant functions and structures for ROM boot-loader. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base_flash.h" + + +#define ROMBOOT_DELAY_MS (20) + +/** + * @section: Structure to assemble flash command + */ +struct flash_param { + union { + struct { + unsigned char byte0; + unsigned char byte1; + unsigned char byte2; + }; + struct { + unsigned char spi_param; + unsigned char clk_div; + unsigned char mode; + }; + }; + unsigned char read_size[2]; + unsigned char command; +}; + +/** + * @section: Specific data blob for romboot + * + * The structure contains various parameters being used in + * ROM boot control + */ +struct tcm_romboot_data_blob { + /* binary data to write */ + const unsigned char *bdata; + unsigned int bdata_size; + /* parsed data based on given binary data */ + struct ihex_info ihex_info; + struct image_info image_info; + /* standard information for flash access */ + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + /* temporary buffer during the reflash */ + struct tcm_buffer out; +}; + + +/** + * syna_tcm_romboot_do_ihex_update() + * + * The entry function to perform ihex update upon ROM Boot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] ihex: ihex data to write + * [ in] ihex_size: size of ihex data + * [ in] flash_size: size for temporary buffer allocation, which used + * to re-order the flash data. + * in general, (ihex_size + 4K) is preferred size + * [ in] len_per_line: length per line in the ihex file + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: flag to indicate a multi-chip product used + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev, + const unsigned char *ihex, unsigned int ihex_size, + unsigned int flash_size, unsigned int len_per_line, + unsigned int delay_ms, bool is_multichip); + +/** + * syna_tcm_romboot_do_multichip_reflash() + * + * The entry function to perform fw update with multi-chip product. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * set [erase_delay_ms | write_delay_ms] for setup; + * set '0' to use default time; + * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven. + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash); + +/** + * syna_tcm_get_romboot_info() + * + * Implement the bootloader command code, which is used to request a + * RomBoot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rom_boot_info: the romboot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev, + struct tcm_romboot_info *rom_boot_info); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ */ diff --git a/tcm/synaptics_touchcom_func_touch.c b/tcm/synaptics_touchcom_func_touch.c new file mode 100644 index 0000000..f390d0c --- /dev/null +++ b/tcm/synaptics_touchcom_func_touch.c @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_tcm2_func_touch.c + * + * This file implements the touch report handling functions. + * The declarations are available in synaptics_touchcom_func_touch.h. + */ + +#include "synaptics_touchcom_func_touch.h" + +/** + * syna_tcm_get_touch_data() + * + * Get data entity from the received report according to bit offset and bit + * length defined in the touch report configuration. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: number of bits representing the data + * [out] data: data parsed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_touch_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, unsigned int *data) +{ + unsigned char mask; + unsigned char byte_data; + unsigned int output_data; + unsigned int bit_offset; + unsigned int byte_offset; + unsigned int data_bits; + unsigned int available_bits; + unsigned int remaining_bits; + + if (bits == 0 || bits > 32) { + LOGE("Invalid number of bits %d\n", bits); + return _EINVAL; + } + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (offset + bits > report_size * 8) { + *data = 0; + return 0; + } + + output_data = 0; + remaining_bits = bits; + + bit_offset = offset % 8; + byte_offset = offset / 8; + + while (remaining_bits) { + byte_data = report[byte_offset]; + byte_data >>= bit_offset; + + available_bits = 8 - bit_offset; + data_bits = MIN(available_bits, remaining_bits); + mask = 0xff >> (8 - data_bits); + + byte_data &= mask; + + output_data |= byte_data << (bits - remaining_bits); + + bit_offset = 0; + byte_offset += 1; + remaining_bits -= data_bits; + } + + *data = output_data; + + return 0; +} + +/** + * syna_tcm_get_gesture_data() + * + * The contents of the gesture data entity depend on which gesture + * is detected. The default size of data is defined in 16-64 bits natively. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: total bits representing the gesture data + * [out] gesture_data: gesture data parsed + * [ in] gesture_id: gesture id retrieved + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_get_gesture_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, struct tcm_gesture_data_blob *gesture_data, + unsigned int gesture_id) +{ + int retval; + unsigned int idx; + unsigned int data; + unsigned int size; + unsigned int data_end; + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (offset + bits > report_size * 8) + return 0; + + data_end = offset + bits; + + size = (sizeof(gesture_data->data) / sizeof(unsigned char)); + + idx = 0; + while ((offset < data_end) && (idx < size)) { + retval = syna_tcm_get_touch_data(report, report_size, + offset, 16, &data); + if (retval < 0) { + LOGE("Fail to get object index\n"); + return retval; + } + gesture_data->data[idx++] = (unsigned char)(data & 0xff); + gesture_data->data[idx++] = (unsigned char)((data >> 8) & 0xff); + offset += 16; + } + + switch (gesture_id) { + case GESTURE_ID_DOUBLE_TAP: + case GESTURE_ID_ACTIVE_TAP_AND_HOLD: + LOGD("Tap info: (%d, %d)\n", + syna_pal_le2_to_uint(gesture_data->tap_x), + syna_pal_le2_to_uint(gesture_data->tap_y)); + break; + case GESTURE_ID_SWIPE: + LOGD("Swipe info: direction:%x (%d, %d)\n", + syna_pal_le2_to_uint(gesture_data->swipe_direction), + syna_pal_le2_to_uint(gesture_data->tap_x), + syna_pal_le2_to_uint(gesture_data->tap_y)); + break; + default: + LOGW("Unknown gesture_id:%d\n", gesture_id); + break; + } + + return 0; +} + +/** + * syna_tcm_parse_touch_report() + * + * Traverse through touch report configuration and parse the contents of + * report packet to get the exactly touched data entity from touch reports. + * + * At the end of function, the touched data will be parsed and stored at the + * associated position in structure touch_data_blob. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [out] touch_data: touch data generated + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev, + unsigned char *report, unsigned int report_size, + struct tcm_touch_data_blob *touch_data) +{ + int retval; + bool active_only; + bool num_of_active_objects; + unsigned char code; + unsigned int size; + unsigned int idx; + unsigned int obj; + unsigned int next; + unsigned int data; + unsigned int bits; + unsigned int offset; + unsigned int objects; + unsigned int active_objects; + unsigned int config_size; + unsigned char *config_data; + struct tcm_objects_data_blob *object_data; + static unsigned int end_of_foreach; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (report_size <= 0) { + LOGE("Invalid report data length\n"); + return _EINVAL; + } + + if (!touch_data) { + LOGE("Invalid touch data structure\n"); + return _EINVAL; + } + + if (tcm_dev->max_objects == 0) { + LOGE("Invalid max_objects supported\n"); + return _EINVAL; + } + + object_data = touch_data->object_data; + + if (!object_data) { + LOGE("Invalid object_data\n"); + return _EINVAL; + } + + config_data = tcm_dev->touch_config.buf; + config_size = tcm_dev->touch_config.data_length; + + if ((!config_data) || (config_size == 0)) { + LOGE("Invalid config_data\n"); + return _EINVAL; + } + + size = sizeof(touch_data->object_data); + syna_pal_mem_set(touch_data->object_data, 0x00, size); + + num_of_active_objects = false; + + idx = 0; + offset = 0; + objects = 0; + active_objects = 0; + active_only = false; + obj = 0; + next = 0; + + while (idx < config_size) { + code = config_data[idx++]; + switch (code) { + case TOUCH_REPORT_END: + goto exit; + case TOUCH_REPORT_FOREACH_ACTIVE_OBJECT: + obj = 0; + next = idx; + active_only = true; + break; + case TOUCH_REPORT_FOREACH_OBJECT: + obj = 0; + next = idx; + active_only = false; + break; + case TOUCH_REPORT_FOREACH_END: + end_of_foreach = idx; + if (active_only) { + if (num_of_active_objects) { + objects++; + obj++; + if (objects < active_objects) + idx = next; + } else if (offset < report_size * 8) { + obj++; + idx = next; + } + } else { + obj++; + if (obj < tcm_dev->max_objects) + idx = next; + } + break; + case TOUCH_REPORT_PAD_TO_NEXT_BYTE: + offset = syna_pal_ceil_div(offset, 8) * 8; + break; + case TOUCH_REPORT_TIMESTAMP: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get time-stamp\n"); + return retval; + } + touch_data->timestamp = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_INDEX: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object index\n"); + return retval; + } + obj = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_CLASSIFICATION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object classification\n"); + return retval; + } + object_data[obj].status = (unsigned char)data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_X_POSITION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object x position\n"); + return retval; + } + object_data[obj].x_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Y_POSITION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object y position\n"); + return retval; + } + object_data[obj].y_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Z: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object z\n"); + return retval; + } + object_data[obj].z = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_X_WIDTH: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object x width\n"); + return retval; + } + object_data[obj].x_width = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Y_WIDTH: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object y width\n"); + return retval; + } + object_data[obj].y_width = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object tx position\n"); + return retval; + } + object_data[obj].tx_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object rx position\n"); + return retval; + } + object_data[obj].rx_pos = data; + offset += bits; + break; + case TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get number of active objects\n"); + return retval; + } + active_objects = data; + num_of_active_objects = true; + touch_data->num_of_active_objects = data; + offset += bits; + if (touch_data->num_of_active_objects == 0) { + if (end_of_foreach == 0) { + LOGE("Invalid end_foreach\n"); + return 0; + } + idx = end_of_foreach; + } + break; + case TOUCH_REPORT_0D_BUTTONS_STATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get 0D buttons state\n"); + return retval; + } + touch_data->buttons_state = data; + offset += bits; + break; + case TOUCH_REPORT_GESTURE_ID: + if (tcm_dev->custom_gesture_parse_func) { + retval = tcm_dev->custom_gesture_parse_func( + TOUCH_REPORT_GESTURE_ID, config_data, + &idx, report, &offset, report_size, + tcm_dev->cbdata_gesture_parse); + } else { + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, + report_size, offset, bits, &data); + touch_data->gesture_id = data; + offset += bits; + } + if (retval < 0) { + LOGE("Fail to get gesture id\n"); + return retval; + } + break; + case TOUCH_REPORT_GESTURE_DATA: + if (tcm_dev->custom_gesture_parse_func) { + retval = tcm_dev->custom_gesture_parse_func( + TOUCH_REPORT_GESTURE_DATA, config_data, + &idx, report, &offset, report_size, + tcm_dev->cbdata_gesture_parse); + } else { + bits = config_data[idx++]; + retval = syna_tcm_get_gesture_data(report, + report_size, + offset, bits, + &touch_data->gesture_data, + touch_data->gesture_id); + offset += bits; + } + if (retval < 0) { + LOGE("Fail to get gesture data\n"); + return retval; + } + break; + case TOUCH_REPORT_FRAME_RATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get frame rate\n"); + return retval; + } + touch_data->frame_rate = data; + offset += bits; + break; + case TOUCH_REPORT_FORCE_MEASUREMENT: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get force measurement data\n"); + return retval; + } + touch_data->force_data = data; + offset += bits; + break; + case TOUCH_REPORT_FINGERPRINT_AREA_MEET: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get data for fingerprint area\n"); + return retval; + } + touch_data->fingerprint_area_meet = data; + offset += bits; + break; + case TOUCH_REPORT_POWER_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get power IM\n"); + return retval; + } + touch_data->power_im = data; + offset += bits; + break; + case TOUCH_REPORT_CID_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get CID IM\n"); + return retval; + } + touch_data->cid_im = data; + offset += bits; + break; + case TOUCH_REPORT_RAIL_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get rail IM\n"); + return retval; + } + touch_data->rail_im = data; + offset += bits; + break; + case TOUCH_REPORT_CID_VARIANCE_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get CID variance IM\n"); + return retval; + } + touch_data->cid_variance_im = data; + offset += bits; + break; + case TOUCH_REPORT_NSM_FREQUENCY_INDEX: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get NSM frequency\n"); + return retval; + } + touch_data->nsm_frequency = data; + offset += bits; + break; + case TOUCH_REPORT_NSM_STATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get NSM state\n"); + return retval; + } + touch_data->nsm_state = data; + offset += bits; + break; + case TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get cpu cycles info\n"); + return retval; + } + touch_data->num_of_cpu_cycles = data; + offset += bits; + break; + case TOUCH_REPORT_FACE_DETECT: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to detect face\n"); + return retval; + } + touch_data->fd_data = data; + offset += bits; + break; + case TOUCH_REPORT_SENSING_MODE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get sensing mode\n"); + return retval; + } + touch_data->sensing_mode = data; + offset += bits; + break; + default: + /* use custom parsing method, if registered */ + if (tcm_dev->custom_touch_data_parse_func) { + retval = tcm_dev->custom_touch_data_parse_func( + code, config_data, &idx, report, + &offset, report_size, + tcm_dev->cbdata_touch_data_parse); + if (retval >= 0) + continue; + } + + LOGW("Unknown touch config code:0x%02x (length:%d)\n", + code, config_data[idx]); + bits = config_data[idx++]; + offset += bits; + break; + } + } + +exit: + return 0; +} + +/** + * syna_tcm_set_touch_report_config() + * + * Setup the format and content of touch report if needed. + * + * TouchComm allows to set how touch reports are formatted and what items get + * reported each time a touch report is generated. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config: the customized report configuration + * [ in] config_size: size of given config + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev, + unsigned char *config, unsigned int config_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + unsigned char *data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!config) || (config_size == 0)) { + LOGE("Invalid given config data\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Not in application fw mode, mode: %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + size = syna_pal_le2_to_uint(app_info->max_touch_report_config_size); + + if (config_size > size) { + LOGE("Invalid config size: %d (max: %d)\n", config_size, size); + return _EINVAL; + } + + data = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!data) { + LOGE("Fail to allocate memory for touch config setting\n"); + return _ENOMEM; + } + + retval = syna_pal_mem_cpy(data, + size, + config, + config_size, + config_size); + if (retval < 0) { + LOGE("Fail to copy custom touch config\n"); + goto exit; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_TOUCH_REPORT_CONFIG, + data, + size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to write command CMD_SET_TOUCH_REPORT_CONFIG\n"); + goto exit; + } + + LOGI("Set touch config done\n"); + +exit: + if (data) + syna_pal_mem_free((void *)data); + + return retval; +} + +/** + * syna_tcm_preserve_touch_report_config() + * + * Retrieve and preserve the current touch report configuration. + * + * The retrieved configuration is stored in touch_config buffer defined + * in structure syna_tcm_dev for later using of touch position parsing. + * + * The touch_config buffer will be allocated internally and its size will + * be updated accordingly. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Not in application fw mode, mode: %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_TOUCH_REPORT_CONFIG, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to write command CMD_GET_TOUCH_REPORT_CONFIG\n"); + goto exit; + } + + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + size = tcm_dev->resp_buf.data_length; + retval = syna_tcm_buf_alloc(&tcm_dev->touch_config, + size); + if (retval < 0) { + LOGE("Fail to allocate memory for internal touch_config\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_dev->touch_config); + + retval = syna_pal_mem_cpy(tcm_dev->touch_config.buf, + tcm_dev->touch_config.buf_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + size); + if (retval < 0) { + LOGE("Fail to clone touch config\n"); + syna_tcm_buf_unlock(&tcm_dev->touch_config); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + tcm_dev->touch_config.data_length = size; + + syna_tcm_buf_unlock(&tcm_dev->touch_config); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + +exit: + return retval; +} + +/** + * syna_tcm_set_custom_touch_data_parsing_callback() + * + * Set up callback function to handle custom touch data. + * + * Once finding the "new" custom entity in touch report, the core library will + * invoke the custom parsing method to handle this "new" code entity. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_touch_data_parsing_callback(struct tcm_dev *tcm_dev, + tcm_touch_data_parse_callback_t p_cb, void *p_cbdata) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->custom_touch_data_parse_func = p_cb; + tcm_dev->cbdata_touch_data_parse = p_cbdata; + + LOGI("enabled\n"); + + return 0; +} + +/** + * syna_tcm_set_custom_gesture_parsing_callback() + * + * Set up callback function to handle the gesture data defined as the following + * code entities + * - TOUCH_REPORT_GESTURE_ID + * - TOUCH_REPORT_GESTURE_DATA + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_gesture_parsing_callback(struct tcm_dev *tcm_dev, + tcm_gesture_parse_callback_t p_cb, void *p_cbdata) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->custom_gesture_parse_func = p_cb; + tcm_dev->cbdata_gesture_parse = p_cbdata; + + LOGI("enabled\n"); + + return 0; +} + diff --git a/tcm/synaptics_touchcom_func_touch.h b/tcm/synaptics_touchcom_func_touch.h new file mode 100644 index 0000000..eb63c05 --- /dev/null +++ b/tcm/synaptics_touchcom_func_touch.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +/** + * @file synaptics_tcm2_func_touch.h + * + * This file declares related APIs and definitions for touch report handling. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * @section: Types of Object Reported + * + * List the object classifications + */ +enum object_classification { + LIFT = 0, + FINGER = 1, + GLOVED_OBJECT = 2, + STYLUS = 3, + ERASER = 4, + SMALL_OBJECT = 5, + PALM = 6, + EDGE_TOUCHED = 8, + HOVER_OBJECT = 9, + NOP = -1, +}; + +/** + * @section: Types of Gesture ID + * + * List the gesture ID assigned + */ +enum gesture_classification { + GESTURE_ID_NONE = 0, + GESTURE_ID_DOUBLE_TAP = 1, + GESTURE_ID_SWIPE = 2, + GESTURE_ID_ACTIVE_TAP_AND_HOLD = 7, +}; + +/** + * @section: Codes for Touch Report Configuration + * + * Define the 8-bit codes for the touch report configuration + */ +enum touch_report_code { + /* control flow codes */ + TOUCH_REPORT_END = 0x00, + TOUCH_REPORT_FOREACH_ACTIVE_OBJECT = 0x01, + TOUCH_REPORT_FOREACH_OBJECT = 0x02, + TOUCH_REPORT_FOREACH_END = 0x03, + TOUCH_REPORT_PAD_TO_NEXT_BYTE = 0x04, + /* entity codes */ + TOUCH_REPORT_TIMESTAMP = 0x05, + TOUCH_REPORT_OBJECT_N_INDEX = 0x06, + TOUCH_REPORT_OBJECT_N_CLASSIFICATION = 0x07, + TOUCH_REPORT_OBJECT_N_X_POSITION = 0x08, + TOUCH_REPORT_OBJECT_N_Y_POSITION = 0x09, + TOUCH_REPORT_OBJECT_N_Z = 0x0a, + TOUCH_REPORT_OBJECT_N_X_WIDTH = 0x0b, + TOUCH_REPORT_OBJECT_N_Y_WIDTH = 0x0c, + TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS = 0x0d, + TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS = 0x0e, + TOUCH_REPORT_0D_BUTTONS_STATE = 0x0f, + TOUCH_REPORT_GESTURE_ID = 0x10, + TOUCH_REPORT_FRAME_RATE = 0x11, + TOUCH_REPORT_POWER_IM = 0x12, + TOUCH_REPORT_CID_IM = 0x13, + TOUCH_REPORT_RAIL_IM = 0x14, + TOUCH_REPORT_CID_VARIANCE_IM = 0x15, + TOUCH_REPORT_NSM_FREQUENCY_INDEX = 0x16, + TOUCH_REPORT_NSM_STATE = 0x17, + TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS = 0x18, + TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME = 0x19, + TOUCH_REPORT_FACE_DETECT = 0x1a, + TOUCH_REPORT_GESTURE_DATA = 0x1b, + TOUCH_REPORT_FORCE_MEASUREMENT = 0x1c, + TOUCH_REPORT_FINGERPRINT_AREA_MEET = 0x1d, + TOUCH_REPORT_SENSING_MODE = 0x1e, + TOUCH_REPORT_KNOB_DATA = 0x24, +}; + +/** + * syna_tcm_parse_touch_report() + * + * Traverse through touch report configuration and parse the contents of + * report packet to get the exactly touched data entity from touch reports. + * + * At the end of function, the touched data will be parsed and stored at the + * associated position in struct touch_data_blob. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [out] touch_data: touch data generated + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev, + unsigned char *report, unsigned int report_size, + struct tcm_touch_data_blob *touch_data); + +/** + * syna_tcm_set_touch_report_config() + * + * Setup the format and content of touch report if needed + * + * TouchComm allows to set how touch reports are formatted and what items get + * reported each time a touch report is generated. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config: the customized report configuration + * [ in] config_size: size of given config + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev, + unsigned char *config, unsigned int config_size); + +/** + * syna_tcm_preserve_touch_report_config() + * + * Retrieve and preserve the current touch report configuration. + * + * The retrieved configuration is stored in touch_config buffer defined + * in struct syna_tcm_dev for later using of touch position parsing. + * + * The touch_config buffer will be allocated internally and its size will + * be updated accordingly. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev); + + +/** + * syna_tcm_get_touch_data() + * + * Get data entity from the received report according to bit offset and bit + * length defined in the touch report configuration. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: number of bits representing the data + * [out] data: data parsed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_touch_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, unsigned int *data); + +/** + * syna_tcm_set_custom_touch_data_parsing_callback() + * + * Set up callback function to handle custom touch data. + * + * Once finding the "new" custom entity in touch report, the core library will + * invoke the custom parsing method to handle this "new" code entity. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_touch_data_parsing_callback(struct tcm_dev *tcm_dev, + tcm_touch_data_parse_callback_t p_cb, void *p_cbdata); + +/** + * syna_tcm_set_custom_gesture_parsing_callback() + * + * Set up callback function to handle the gesture data defined as the following + * code entities + * - TOUCH_REPORT_GESTURE_ID + * - TOUCH_REPORT_GESTURE_DATA + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_gesture_parsing_callback(struct tcm_dev *tcm_dev, + tcm_gesture_parse_callback_t p_cb, void *p_cbdata); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ */ -- cgit v1.2.3